Remove clang-modernize.

Summary:
clang-modernize transforms have moved to clang-tidy. Removing
the old tool now.

Reviewers: klimek

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D15606

llvm-svn: 255886
This commit is contained in:
Alexander Kornienko 2015-12-17 11:49:19 +00:00
parent 0e59c516c4
commit 23f04fd469
148 changed files with 6 additions and 14152 deletions

View File

@ -1,5 +1,4 @@
add_subdirectory(clang-apply-replacements)
add_subdirectory(clang-modernize)
add_subdirectory(clang-rename)
add_subdirectory(modularize)
if(CLANG_ENABLE_STATIC_ANALYZER)

View File

@ -12,8 +12,7 @@ CLANG_LEVEL := ../..
include $(CLANG_LEVEL)/../../Makefile.config
PARALLEL_DIRS := tool-template modularize pp-trace
DIRS := clang-apply-replacements clang-modernize clang-rename clang-tidy \
clang-query unittests
DIRS := clang-apply-replacements clang-rename clang-tidy clang-query unittests
include $(CLANG_LEVEL)/Makefile

View File

@ -1,85 +0,0 @@
//===-- AddOverride/AddOverride.cpp - add C++11 override ------------------===//
//
// 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 AddOverrideTransform
/// class.
///
//===----------------------------------------------------------------------===//
#include "AddOverride.h"
#include "AddOverrideActions.h"
#include "AddOverrideMatchers.h"
#include "clang/Frontend/CompilerInstance.h"
using clang::ast_matchers::MatchFinder;
using namespace clang::tooling;
using namespace clang;
namespace cl = llvm::cl;
static cl::opt<bool> DetectMacros(
"override-macros",
cl::desc("Detect and use macros that expand to the 'override' keyword."),
cl::cat(TransformsOptionsCategory));
int AddOverrideTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool AddOverrideTool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
MatchFinder Finder;
AddOverrideFixer Fixer(AcceptedChanges, DetectMacros,
/*Owner=*/ *this);
Finder.addMatcher(makeCandidateForOverrideAttrMatcher(), &Fixer);
// Make Fixer available to handleBeginSource().
this->Fixer = &Fixer;
if (int result = AddOverrideTool.run(createActionFactory(Finder).get())) {
llvm::errs() << "Error encountered during translation.\n";
return result;
}
setAcceptedChanges(AcceptedChanges);
return 0;
}
bool AddOverrideTransform::handleBeginSource(clang::CompilerInstance &CI,
llvm::StringRef Filename) {
assert(Fixer != nullptr && "Fixer must be set");
Fixer->setPreprocessor(CI.getPreprocessor());
return Transform::handleBeginSource(CI, Filename);
}
namespace {
struct AddOverrideFactory : TransformFactory {
AddOverrideFactory() {
// if detecting macros is enabled, do not impose requirements on the
// compiler. It is assumed that the macros use is "C++11-aware", meaning it
// won't expand to override if the compiler doesn't support the specifier.
if (!DetectMacros) {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 7);
Since.Icc = Version(14);
Since.Msvc = Version(8);
}
}
Transform *createTransform(const TransformOptions &Opts) override {
return new AddOverrideTransform(Opts);
}
};
} // namespace
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<AddOverrideFactory>
X("add-override", "Make use of override specifier where possible");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int AddOverrideTransformAnchorSource = 0;

View File

@ -1,44 +0,0 @@
//===-- AddOverride/AddOverride.h - add C++11 override ----------*- 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 of the AddOverrideTransform
/// class which is the main interface to the transform that tries to add the
/// override keyword to declarations of member function that override virtual
/// functions in a base class.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_ADD_OVERRIDE_H
#define CLANG_MODERNIZE_ADD_OVERRIDE_H
#include "Core/Transform.h"
#include "llvm/Support/Compiler.h"
class AddOverrideFixer;
/// \brief Subclass of Transform that adds the C++11 override keyword to
/// member functions overriding base class virtual functions.
class AddOverrideTransform : public Transform {
public:
AddOverrideTransform(const TransformOptions &Options)
: Transform("AddOverride", Options) {}
/// \see Transform::run().
int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) override;
bool handleBeginSource(clang::CompilerInstance &CI,
llvm::StringRef Filename) override;
private:
AddOverrideFixer *Fixer;
};
#endif // CLANG_MODERNIZE_ADD_OVERRIDE_H

View File

@ -1,100 +0,0 @@
//===-- AddOverride/AddOverrideActions.cpp - add C++11 override -----------===//
//
// 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 contains the definition of the AddOverrideFixer class
/// which is used as an ASTMatcher callback.
///
//===----------------------------------------------------------------------===//
#include "AddOverrideActions.h"
#include "AddOverrideMatchers.h"
#include "Core/Transform.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace clang;
namespace {
SourceLocation
backwardSkipWhitespacesAndComments(const SourceManager &SM,
const clang::ASTContext &Context,
SourceLocation Loc) {
for (;;) {
do {
Loc = Loc.getLocWithOffset(-1);
} while (isWhitespace(*FullSourceLoc(Loc, SM).getCharacterData()));
Token Tok;
SourceLocation Beginning =
Lexer::GetBeginningOfToken(Loc, SM, Context.getLangOpts());
const bool Invalid =
Lexer::getRawToken(Beginning, Tok, SM, Context.getLangOpts());
assert(!Invalid && "Expected a valid token.");
if (Invalid || Tok.getKind() != tok::comment)
return Loc.getLocWithOffset(1);
}
}
} // end anonymous namespace
void AddOverrideFixer::run(const MatchFinder::MatchResult &Result) {
SourceManager &SM = *Result.SourceManager;
const CXXMethodDecl *M = Result.Nodes.getDeclAs<CXXMethodDecl>(MethodId);
assert(M && "Bad Callback. No node provided");
if (const FunctionDecl *TemplateMethod = M->getTemplateInstantiationPattern())
M = cast<CXXMethodDecl>(TemplateMethod);
if (!Owner.isFileModifiable(SM, M->getLocStart()))
return;
// First check that there isn't already an override attribute.
if (M->hasAttr<OverrideAttr>())
return;
// FIXME: Pure methods are not supported yet as it is difficult to track down
// the location of '= 0'.
if (M->isPure())
return;
if (M->getParent()->hasAnyDependentBases())
return;
SourceLocation StartLoc;
if (M->hasInlineBody()) {
// Insert the override specifier before the function body.
StartLoc = backwardSkipWhitespacesAndComments(SM, *Result.Context,
M->getBody()->getLocStart());
} else {
StartLoc = SM.getSpellingLoc(M->getLocEnd());
StartLoc = Lexer::getLocForEndOfToken(StartLoc, 0, SM, LangOptions());
}
std::string ReplacementText = " override";
if (DetectMacros) {
assert(PP && "No access to Preprocessor object for macro detection");
clang::TokenValue Tokens[] = { PP->getIdentifierInfo("override") };
llvm::StringRef MacroName = PP->getLastMacroWithSpelling(StartLoc, Tokens);
if (!MacroName.empty())
ReplacementText = (" " + MacroName).str();
}
Owner.addReplacementForCurrentTU(
tooling::Replacement(SM, StartLoc, 0, ReplacementText));
++AcceptedChanges;
}

View File

@ -1,46 +0,0 @@
//===-- AddOverride/AddOverrideActions.h - add C++11 override ---*- 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 contains the declaration of the AddOverrideFixer class
/// which is used as a ASTMatcher callback.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_ADD_OVERRIDE_ACTIONS_H
#define CLANG_MODERNIZE_ADD_OVERRIDE_ACTIONS_H
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Refactoring.h"
class Transform;
/// \brief The callback to be used for add-override migration matchers.
///
class AddOverrideFixer : public clang::ast_matchers::MatchFinder::MatchCallback {
public:
AddOverrideFixer(unsigned &AcceptedChanges, bool DetectMacros,
Transform &Owner)
: AcceptedChanges(AcceptedChanges), DetectMacros(DetectMacros),
Owner(Owner) {}
/// \brief Entry point to the callback called when matches are made.
void
run(const clang::ast_matchers::MatchFinder::MatchResult &Result) override;
void setPreprocessor(clang::Preprocessor &PP) { this->PP = &PP; }
private:
clang::Preprocessor *PP;
unsigned &AcceptedChanges;
bool DetectMacros;
Transform &Owner;
};
#endif // CLANG_MODERNIZE_ADD_OVERRIDE_ACTIONS_H

View File

@ -1,29 +0,0 @@
//===-- AddOverride/AddOverrideMatchers.cpp - C++11 override --------------===//
//
// 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 contains the definitions for matcher-generating functions
/// and a custom AST_MATCHER for identifying casts of type CK_NullTo*.
///
//===----------------------------------------------------------------------===//
#include "AddOverrideMatchers.h"
#include "clang/AST/ASTContext.h"
using namespace clang::ast_matchers;
using namespace clang;
const char *MethodId = "method";
DeclarationMatcher makeCandidateForOverrideAttrMatcher() {
return cxxMethodDecl(hasParent(recordDecl()),
isOverride(),
unless(cxxDestructorDecl())).bind(MethodId);
}

View File

@ -1,28 +0,0 @@
//===-- AddOverride/AddOverrideMatchers.h - add C++11 override --*- 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 contains the declarations for matcher-generating functions
/// and names for bound nodes found by AST matchers.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_ADD_OVERRIDE_MATCHERS_H
#define CLANG_MODERNIZE_ADD_OVERRIDE_MATCHERS_H
#include "clang/ASTMatchers/ASTMatchers.h"
/// Name to bind with matched expressions.
extern const char *MethodId;
/// \brief Create a matcher that finds member function declarations that are
/// candidates for adding the override attribute.
clang::ast_matchers::DeclarationMatcher makeCandidateForOverrideAttrMatcher();
#endif // CLANG_MODERNIZE_ADD_OVERRIDE_MATCHERS_H

View File

@ -1,7 +0,0 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${ClangReplaceLocation}
)
add_subdirectory(tool)
add_subdirectory(Core)

View File

@ -1,19 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(modernizeCore
ReplacementHandling.cpp
Transforms.cpp
Transform.cpp
IncludeExcludeInfo.cpp
PerfSupport.cpp
IncludeDirectives.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangFrontend
clangLex
clangTooling
clangToolingCore
)

View File

@ -1,59 +0,0 @@
//===-- 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 CLANG_MODERNIZE_CUSTOMMATCHERS_H
#define CLANG_MODERNIZE_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 // CLANG_MODERNIZE_CUSTOMMATCHERS_H

View File

@ -1,480 +0,0 @@
//===-- 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(nullptr), 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(nullptr) {}
~IncludeDirectivesPPCallback() override {}
private:
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange, const FileEntry *File,
StringRef SearchPath, StringRef RelativePath,
const Module *Imported) 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
void FileChanged(SourceLocation Loc, FileChangeReason Reason,
SrcMgr::CharacteristicKind FileType,
FileID PrevFID) override {
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)->getBuffer(), 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;
}
void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override {
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) {
IdentifierInfo *MII = MacroNameTok.getIdentifierInfo();
Guard->IfndefLoc = Loc;
Guard->TheMacro = MII;
}
}
void MacroDefined(const Token &MacroNameTok,
const MacroDirective *MD) override {
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 != nullptr) {
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();
}
}
void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
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;
}
void MacroExpands(const Token &, const MacroDefinition &, SourceRange,
const MacroArgs *) override {
Guard->Count++;
}
void MacroUndefined(const Token &, const MacroDefinition &) override {
Guard->Count++;
}
void Defined(const Token &, const MacroDefinition &, SourceRange) override {
Guard->Count++;
}
void If(SourceLocation, SourceRange, ConditionValueKind) override {
Guard->Count++;
}
void Elif(SourceLocation, SourceRange, ConditionValueKind,
SourceLocation) override {
Guard->Count++;
}
void Ifdef(SourceLocation, const Token &, const MacroDefinition &) override {
Guard->Count++;
}
void Else(SourceLocation, SourceLocation) override {
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(
llvm::make_unique<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.isValid() && "Invalid file entry given!");
if (hasInclude(File, Include))
return Replacement();
unsigned Offset, NLFlags;
std::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);
}

View File

@ -1,141 +0,0 @@
//===-- 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 CLANG_MODERNIZE_INCLUDE_DIRECTIVES_H
#define CLANG_MODERNIZE_INCLUDE_DIRECTIVES_H
#include "clang/Basic/SourceLocation.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringRef.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 \parblock
/// \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 preferably 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 // CLANG_MODERNIZE_INCLUDE_DIRECTIVES_H

View File

@ -1,179 +0,0 @@
//===-- 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.
std::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 (std::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 std::error_code();
}
} // end anonymous namespace
std::error_code
IncludeExcludeInfo::readListFromString(StringRef IncludeString,
StringRef ExcludeString) {
if (std::error_code Err = parseCLInput(IncludeString, IncludeList,
/*Separator=*/","))
return Err;
if (std::error_code Err = parseCLInput(ExcludeString, ExcludeList,
/*Separator=*/","))
return Err;
return std::error_code();
}
std::error_code
IncludeExcludeInfo::readListFromFile(StringRef IncludeListFile,
StringRef ExcludeListFile) {
if (!IncludeListFile.empty()) {
ErrorOr<std::unique_ptr<MemoryBuffer>> FileBuf =
MemoryBuffer::getFile(IncludeListFile);
if (std::error_code Err = FileBuf.getError()) {
errs() << "Unable to read from include file.\n";
return Err;
}
if (std::error_code Err =
parseCLInput(FileBuf.get()->getBuffer(), IncludeList,
/*Separator=*/"\n"))
return Err;
}
if (!ExcludeListFile.empty()) {
ErrorOr<std::unique_ptr<MemoryBuffer>> FileBuf =
MemoryBuffer::getFile(ExcludeListFile);
if (std::error_code Err = FileBuf.getError()) {
errs() << "Unable to read from exclude file.\n";
return Err;
}
if (std::error_code Err =
parseCLInput(FileBuf.get()->getBuffer(), ExcludeList,
/*Separator=*/"\n"))
return Err;
}
return std::error_code();
}
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;
// If the file is in the included list but not is not explicitly excluded,
// then it is safe to transform.
return !isFileExplicitlyExcluded(FilePath);
}
bool IncludeExcludeInfo::isFileExplicitlyExcluded(StringRef FilePath) const {
for (std::vector<std::string>::const_iterator I = ExcludeList.begin(),
E = ExcludeList.end();
I != E; ++I)
if (fileHasPathPrefix(FilePath, *I))
return true;
return false;
}

View File

@ -1,62 +0,0 @@
//===-- 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 CLANG_MODERNIZE_INCLUDEEXCLUDEINFO_H
#define CLANG_MODERNIZE_INCLUDEEXCLUDEINFO_H
#include "llvm/ADT/StringRef.h"
#include <system_error>
#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-separated 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.
std::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.
std::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;
/// \brief Determine if a file path was explicitly excluded.
bool isFileExplicitlyExcluded(llvm::StringRef FilePath) const;
/// \brief Determine if a list of include paths was provided.
bool isIncludeListEmpty() const { return IncludeList.empty(); }
private:
std::vector<std::string> IncludeList;
std::vector<std::string> ExcludeList;
};
#endif // CLANG_MODERNIZE_INCLUDEEXCLUDEINFO_H

View File

@ -1,14 +0,0 @@
##===- 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 := modernizeCore
include $(CLANG_LEVEL)/Makefile
CPP.Flags += -I$(PROJ_SRC_DIR)/.. -I$(PROJ_SRC_DIR)/../../clang-apply-replacements/include

View File

@ -1,101 +0,0 @@
//===-- 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/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/raw_ostream.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::error_code EC;
llvm::raw_fd_ostream FileStream(SS.str(), EC, llvm::sys::fs::F_Text);
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";
}
}
}

View File

@ -1,56 +0,0 @@
//===-- 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 CLANG_MODERNIZE_PERFSUPPORT_H
#define CLANG_MODERNIZE_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 // CLANG_MODERNIZE_PERFSUPPORT_H

View File

@ -1,31 +0,0 @@
//===-- 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 CLANG_MODERNIZE_REPLACEMENTS_VEC_H
#define CLANG_MODERNIZE_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 // CLANG_MODERNIZE_REPLACEMENTS_VEC_H

View File

@ -1,155 +0,0 @@
//===-- Core/ReplacementHandling.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 implementations for the ReplacementHandling class.
///
//===----------------------------------------------------------------------===//
#include "Core/ReplacementHandling.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include <system_error>
using namespace llvm;
using namespace llvm::sys;
using namespace clang::tooling;
bool ReplacementHandling::findClangApplyReplacements(const char *Argv0) {
ErrorOr<std::string> CARPathOrErr =
findProgramByName("clang-apply-replacements");
if (!CARPathOrErr)
return true;
CARPath = *CARPathOrErr;
static int StaticSymbol;
std::string ClangModernizePath = fs::getMainExecutable(Argv0, &StaticSymbol);
SmallString<128> TestPath = path::parent_path(ClangModernizePath);
path::append(TestPath, "clang-apply-replacements");
if (fs::can_execute(Twine(TestPath)))
CARPath = TestPath.str();
return !CARPath.empty();
}
StringRef ReplacementHandling::useTempDestinationDir() {
DestinationDir = generateTempDir();
return DestinationDir;
}
void ReplacementHandling::enableFormatting(StringRef Style,
StringRef StyleConfigDir) {
DoFormat = true;
FormatStyle = Style;
this->StyleConfigDir = StyleConfigDir;
}
bool ReplacementHandling::serializeReplacements(
const TUReplacementsMap &Replacements) {
assert(!DestinationDir.empty() && "Destination directory not set");
bool Errors = false;
for (TUReplacementsMap::const_iterator I = Replacements.begin(),
E = Replacements.end();
I != E; ++I) {
SmallString<128> ReplacementsFileName;
SmallString<64> Error;
bool Result = generateReplacementsFileName(DestinationDir,
I->getValue().MainSourceFile,
ReplacementsFileName, Error);
if (!Result) {
errs() << "Failed to generate replacements filename:" << Error << "\n";
Errors = true;
continue;
}
std::error_code EC;
raw_fd_ostream ReplacementsFile(ReplacementsFileName, EC, fs::F_None);
if (EC) {
errs() << "Error opening file: " << EC.message() << "\n";
Errors = true;
continue;
}
yaml::Output YAML(ReplacementsFile);
YAML << const_cast<TranslationUnitReplacements &>(I->getValue());
}
return !Errors;
}
bool ReplacementHandling::applyReplacements() {
SmallVector<const char *, 8> Argv;
Argv.push_back(CARPath.c_str());
std::string Style = "--style=" + FormatStyle;
std::string StyleConfig = "--style-config=" + StyleConfigDir;
if (DoFormat) {
Argv.push_back("--format");
Argv.push_back(Style.c_str());
if (!StyleConfigDir.empty())
Argv.push_back(StyleConfig.c_str());
}
Argv.push_back("--remove-change-desc-files");
Argv.push_back(DestinationDir.c_str());
// Argv array needs to be null terminated.
Argv.push_back(nullptr);
std::string ErrorMsg;
bool ExecutionFailed = false;
int ReturnCode = ExecuteAndWait(CARPath.c_str(), Argv.data(),
/* env */ nullptr, /* redirects */ nullptr,
/* secondsToWait */ 0, /* memoryLimit */ 0,
&ErrorMsg, &ExecutionFailed);
if (ExecutionFailed || !ErrorMsg.empty()) {
errs() << "Failed to launch clang-apply-replacements: " << ErrorMsg << "\n";
errs() << "Command Line:\n";
for (const char **I = Argv.begin(), **E = Argv.end(); I != E; ++I) {
if (*I)
errs() << *I << "\n";
}
return false;
}
if (ReturnCode != 0) {
errs() << "clang-apply-replacements failed with return code " << ReturnCode
<< "\n";
return false;
}
return true;
}
std::string ReplacementHandling::generateTempDir() {
SmallString<128> Prefix;
path::system_temp_directory(true, Prefix);
path::append(Prefix, "clang-modernize");
SmallString<128> Result;
fs::createUniqueDirectory(Twine(Prefix), Result);
return Result.str();
}
bool ReplacementHandling::generateReplacementsFileName(
StringRef DestinationDir, StringRef MainSourceFile,
SmallVectorImpl<char> &Result, SmallVectorImpl<char> &Error) {
Error.clear();
SmallString<128> Prefix = DestinationDir;
path::append(Prefix, path::filename(MainSourceFile));
if (std::error_code EC =
fs::createUniqueFile(Prefix + "_%%_%%_%%_%%_%%_%%.yaml", Result)) {
const std::string &Msg = EC.message();
Error.append(Msg.begin(), Msg.end());
return false;
}
return true;
}

View File

@ -1,127 +0,0 @@
//===-- Core/ReplacementHandling.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 defines the ReplacementHandling class which abstracts
/// serialization and application of serialized replacements.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_REPLACEMENTHANDLING_H
#define CLANG_MODERNIZE_REPLACEMENTHANDLING_H
#include "Core/Transform.h"
#include "llvm/ADT/StringRef.h"
class ReplacementHandling {
public:
ReplacementHandling() : DoFormat(false) {}
/// \brief Finds the path to the executable 'clang-apply-replacements'.
///
/// The executable is searched for on the PATH. If not found, looks in the
/// same directory as the image used to start the current executable.
///
/// \param[in] Argv0 argv[0] as passed to main().
///
/// \returns \parblock
/// \li true if clang-apply-replacements was found.
/// \li false otherwise.
bool findClangApplyReplacements(const char *Argv0);
/// \brief Set the name of the directory in which replacements will be
/// serialized.
///
/// \param[in] Dir Destination directory name
void setDestinationDir(llvm::StringRef Dir) { DestinationDir = Dir; }
/// \brief Create a new temporary directory to serialize replacements into.
///
/// \returns The name of the directory createdy.
llvm::StringRef useTempDestinationDir();
/// \brief Enable clang-apply-replacements do code reformatting when applying
/// serialized replacements.
///
/// \param[in] Style Value to pass to clang-apply-replacement's --style
/// option.
/// \param[in] StyleConfigDir If non-empty, value to pass to
/// clang-apply-replacement's --style-config option.
void enableFormatting(llvm::StringRef Style,
llvm::StringRef StyleConfigDir = "");
/// \brief Write all TranslationUnitReplacements stored in \c Replacements
/// to disk.
///
/// \pre Destination directory must have been previously set by calling
/// setDestiantionDir() or useTempDestinationDir().
/// \pre Destination dir must exist.
///
/// \param[in] Replacements Container of replacements to serialize.
///
/// \returns \parblock
/// \li true if all replacements were serialized successfully to
/// disk.
/// \li false otherwise.
bool serializeReplacements(const TUReplacementsMap &Replacements);
/// \brief Invoke clang-apply-replacements to apply all serialized
/// replacements stored in the destination directory.
///
/// \pre Destination directory must have been previously set by calling
/// setDestiantionDir() or useTempDestinationDir().
///
/// \returns \parblock
/// \li true if clang-apply-replacements was successfully launched
/// and successfully completed.
/// \li false otherwise.
bool applyReplacements();
/// \brief Generate a unique filename to store the replacements.
///
/// Generates a unique filename in \c DestinationDir. The filename is generated
/// following this pattern:
///
/// DestinationDir/Prefix_%%_%%_%%_%%_%%_%%.yaml
///
/// where Prefix := llvm::sys::path::filename(MainSourceFile) and all '%' will
/// be replaced by a randomly chosen hex digit.
///
/// \param[in] DestinationDir Directory the unique file should be placed in.
/// \param[in] MainSourceFile Full path to the source file.
/// \param[out] Result The resulting unique filename.
/// \param[out] Error If an error occurs a description of that error is
/// placed in this string.
///
/// \returns \parblock
/// \li true on success
/// \li false if a unique file name could not be created.
static bool generateReplacementsFileName(llvm::StringRef DestinationDir,
llvm::StringRef MainSourceFile,
llvm::SmallVectorImpl<char> &Result,
llvm::SmallVectorImpl<char> &Error);
/// \brief Helper to create a temporary directory name.
///
/// \post The directory named by the returned string exists.
///
/// \returns A temp directory name.
static std::string generateTempDir();
private:
std::string CARPath;
std::string DestinationDir;
bool DoFormat;
std::string FormatStyle;
std::string StyleConfigDir;
};
#endif // CLANG_MODERNIZE_REPLACEMENTHANDLING_H

View File

@ -1,169 +0,0 @@
//===-- 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 "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"
template class llvm::Registry<TransformFactory>;
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) {}
FrontendAction *create() override {
return new FactoryAdaptor(Finder, Owner);
}
private:
class FactoryAdaptor : public ASTFrontendAction {
public:
FactoryAdaptor(MatchFinder &Finder, Transform &Owner)
: Finder(Finder), Owner(Owner) {}
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &,
StringRef) override {
return Finder.newASTConsumer();
}
bool BeginSourceFileAction(CompilerInstance &CI,
StringRef Filename) override {
if (!ASTFrontendAction::BeginSourceFileAction(CI, Filename))
return false;
return Owner.handleBeginSource(CI, Filename);
}
void EndSourceFileAction() 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) {
Reset();
}
Transform::~Transform() {}
bool Transform::isFileModifiable(const SourceManager &SM,
SourceLocation Loc) const {
if (SM.isWrittenInMainFile(Loc))
return true;
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Loc));
if (!FE)
return false;
return GlobalOptions.ModifiableFiles.isFileIncluded(FE->getName());
}
bool Transform::handleBeginSource(CompilerInstance &CI, StringRef Filename) {
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;
}
std::unique_ptr<FrontendActionFactory>
Transform::createActionFactory(MatchFinder &Finder) {
return llvm::make_unique<ActionFactory>(Finder, /*Owner=*/*this);
}
Version Version::getFromString(llvm::StringRef VersionStr) {
llvm::StringRef MajorStr, MinorStr;
Version V;
std::tie(MajorStr, MinorStr) = VersionStr.split('.');
if (!MinorStr.empty()) {
llvm::StringRef Ignore;
std::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);
}

View File

@ -1,329 +0,0 @@
//===-- 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 CLANG_MODERNIZE_TRANSFORM_H
#define CLANG_MODERNIZE_TRANSFORM_H
#include "Core/IncludeExcludeInfo.h"
#include "Core/Refactoring.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
// \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 Contains information on which files are safe to transform and
/// which aren't.
IncludeExcludeInfo ModifiableFiles;
/// \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.
///
/// \param[in] Database Contains information for how to compile all files in
/// \p SourcePaths.
/// \param[in] SourcePaths list of sources to transform.
///
/// \returns \parblock
/// \li 0 if successful
/// \li 1 otherwise
virtual int apply(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,
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 \parblock
/// \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 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().
std::unique_ptr<clang::tooling::FrontendActionFactory>
createActionFactory(clang::ast_matchers::MatchFinder &Finder);
private:
const std::string Name;
const TransformOptions &GlobalOptions;
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) 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;
extern template class llvm::Registry<TransformFactory>;
#endif // CLANG_MODERNIZE_TRANSFORM_H

View File

@ -1,71 +0,0 @@
//===-- 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;
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;
std::unique_ptr<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";
}
}

View File

@ -1,84 +0,0 @@
//===-- 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 CLANG_MODERNIZE_TRANSFORMS_H
#define CLANG_MODERNIZE_TRANSFORMS_H
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.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 To group transforms together when printing the help.
extern llvm::cl::OptionCategory TransformCategory;
/// \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 // CLANG_MODERNIZE_TRANSFORMS_H

File diff suppressed because it is too large Load Diff

View File

@ -1,116 +0,0 @@
//===-- LoopConvert/LoopActions.h - C++11 For loop migration ----*- 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 matchers and callbacks for use in migrating C++
/// for loops.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_LOOP_ACTIONS_H
#define CLANG_MODERNIZE_LOOP_ACTIONS_H
#include "Core/Transform.h"
#include "StmtAncestor.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Refactoring.h"
struct Usage;
class Confidence;
// The main computational result of ForLoopIndexUseVisitor.
typedef llvm::SmallVector<Usage, 8> UsageResult;
enum LoopFixerKind {
LFK_Array,
LFK_Iterator,
LFK_PseudoArray
};
struct TUTrackingInfo {
/// \brief Reset and initialize per-TU tracking information.
///
/// Must be called before using container accessors.
void reset() {
ParentFinder.reset(new StmtAncestorASTVisitor);
GeneratedDecls.clear();
ReplacedVars.clear();
}
/// \name Accessors
/// \{
StmtAncestorASTVisitor &getParentFinder() { return *ParentFinder; }
StmtGeneratedVarNameMap &getGeneratedDecls() { return GeneratedDecls; }
ReplacedVarsMap &getReplacedVars() { return ReplacedVars; }
/// \}
private:
std::unique_ptr<StmtAncestorASTVisitor> ParentFinder;
StmtGeneratedVarNameMap GeneratedDecls;
ReplacedVarsMap ReplacedVars;
};
/// \brief The callback to be used for loop migration matchers.
///
/// The callback does extra checking not possible in matchers, and attempts to
/// convert the for loop, if possible.
class LoopFixer : public clang::ast_matchers::MatchFinder::MatchCallback {
public:
LoopFixer(TUTrackingInfo &TUInfo, unsigned *AcceptedChanges,
unsigned *DeferredChanges, unsigned *RejectedChanges,
RiskLevel MaxRisk, LoopFixerKind FixerKind, Transform &Owner)
: TUInfo(TUInfo), AcceptedChanges(AcceptedChanges),
DeferredChanges(DeferredChanges), RejectedChanges(RejectedChanges),
MaxRisk(MaxRisk), FixerKind(FixerKind), Owner(Owner) {}
void
run(const clang::ast_matchers::MatchFinder::MatchResult &Result) override;
private:
TUTrackingInfo &TUInfo;
unsigned *AcceptedChanges;
unsigned *DeferredChanges;
unsigned *RejectedChanges;
RiskLevel MaxRisk;
LoopFixerKind FixerKind;
Transform &Owner;
/// \brief Computes the changes needed to convert a given for loop, and
/// applies it.
void doConversion(clang::ASTContext *Context, const clang::VarDecl *IndexVar,
const clang::VarDecl *MaybeContainer,
llvm::StringRef ContainerString, const UsageResult &Usages,
const clang::DeclStmt *AliasDecl, bool AliasUseRequired,
bool AliasFromForInit, const clang::ForStmt *TheLoop,
bool ContainerNeedsDereference, bool DerefByValue,
bool DerefByConstRef);
/// \brief Given a loop header that would be convertible, discover all usages
/// of the index variable and convert the loop if possible.
void findAndVerifyUsages(clang::ASTContext *Context,
const clang::VarDecl *LoopVar,
const clang::VarDecl *EndVar,
const clang::Expr *ContainerExpr,
const clang::Expr *BoundExpr,
bool ContainerNeedsDereference, bool DerefByValue,
bool DerefByConstRef, const clang::ForStmt *TheLoop,
Confidence ConfidenceLevel);
/// \brief Determine if the change should be deferred or rejected, returning
/// text which refers to the container iterated over if the change should
/// proceed.
llvm::StringRef checkDeferralsAndRejections(clang::ASTContext *Context,
const clang::Expr *ContainerExpr,
Confidence ConfidenceLevel,
const clang::ForStmt *TheLoop);
};
#endif // CLANG_MODERNIZE_LOOP_ACTIONS_H

View File

@ -1,93 +0,0 @@
//===-- LoopConvert/LoopConvert.cpp - C++11 for-loop migration ------------===//
//
// 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 LoopConvertTransform
/// class.
///
//===----------------------------------------------------------------------===//
#include "LoopConvert.h"
#include "LoopActions.h"
#include "LoopMatchers.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
using clang::ast_matchers::MatchFinder;
using namespace clang::tooling;
using namespace clang;
int LoopConvertTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool LoopTool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
unsigned DeferredChanges = 0;
unsigned RejectedChanges = 0;
TUInfo.reset(new TUTrackingInfo);
MatchFinder Finder;
LoopFixer ArrayLoopFixer(*TUInfo, &AcceptedChanges, &DeferredChanges,
&RejectedChanges, Options().MaxRiskLevel, LFK_Array,
/*Owner=*/ *this);
Finder.addMatcher(makeArrayLoopMatcher(), &ArrayLoopFixer);
LoopFixer IteratorLoopFixer(*TUInfo, &AcceptedChanges, &DeferredChanges,
&RejectedChanges, Options().MaxRiskLevel,
LFK_Iterator, /*Owner=*/ *this);
Finder.addMatcher(makeIteratorLoopMatcher(), &IteratorLoopFixer);
LoopFixer PseudoarrrayLoopFixer(*TUInfo, &AcceptedChanges, &DeferredChanges,
&RejectedChanges, Options().MaxRiskLevel,
LFK_PseudoArray, /*Owner=*/ *this);
Finder.addMatcher(makePseudoArrayLoopMatcher(), &PseudoarrrayLoopFixer);
if (int result = LoopTool.run(createActionFactory(Finder).get())) {
llvm::errs() << "Error encountered during translation.\n";
return result;
}
setAcceptedChanges(AcceptedChanges);
setRejectedChanges(RejectedChanges);
setDeferredChanges(DeferredChanges);
return 0;
}
bool
LoopConvertTransform::handleBeginSource(clang::CompilerInstance &CI,
llvm::StringRef Filename) {
// Reset and initialize per-TU tracking structures.
TUInfo->reset();
return Transform::handleBeginSource(CI, Filename);
}
namespace {
struct LoopConvertFactory : TransformFactory {
LoopConvertFactory() {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 6);
Since.Icc = Version(13);
Since.Msvc = Version(11);
}
Transform *createTransform(const TransformOptions &Opts) override {
return new LoopConvertTransform(Opts);
}
};
} // namespace
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<LoopConvertFactory>
X("loop-convert", "Make use of range-based for loops where possible");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int LoopConvertTransformAnchorSource = 0;

View File

@ -1,44 +0,0 @@
//===-- LoopConvert/LoopConvert.h - C++11 for-loop migration ----*- 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 of the LoopConvertTransform
/// class which is the main interface to the loop-convert transform that tries
/// to make use of range-based for loops where possible.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_LOOP_CONVERT_H
#define CLANG_MODERNIZE_LOOP_CONVERT_H
#include "Core/Transform.h"
#include "llvm/Support/Compiler.h" // For override
// Forward decl for private implementation.
struct TUTrackingInfo;
/// \brief Subclass of Transform that transforms for-loops into range-based
/// for-loops where possible.
class LoopConvertTransform : public Transform {
public:
LoopConvertTransform(const TransformOptions &Options)
: Transform("LoopConvert", Options) {}
/// \see Transform::run().
int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) override;
bool handleBeginSource(clang::CompilerInstance &CI,
llvm::StringRef Filename) override;
private:
std::unique_ptr<TUTrackingInfo> TUInfo;
};
#endif // CLANG_MODERNIZE_LOOP_CONVERT_H

View File

@ -1,345 +0,0 @@
//===-- LoopConvert/LoopMatchers.cpp - Matchers for for loops -------------===//
//
// 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 contains definitions of the matchers for use in migrating
/// C++ for loops.
///
//===----------------------------------------------------------------------===//
#include "LoopMatchers.h"
using namespace clang::ast_matchers;
using namespace clang;
const char LoopName[] = "forLoop";
const char ConditionBoundName[] = "conditionBound";
const char ConditionVarName[] = "conditionVar";
const char IncrementVarName[] = "incrementVar";
const char InitVarName[] = "initVar";
const char BeginCallName[] = "beginCall";
const char EndCallName[] = "endCall";
const char ConditionEndVarName[] = "conditionEndVar";
const char EndVarName[] = "endVar";
const char DerefByValueResultName[] = "derefByValueResult";
const char DerefByRefResultName[] = "derefByRefResult";
// shared matchers
static const TypeMatcher AnyType = anything();
static const StatementMatcher IntegerComparisonMatcher =
expr(ignoringParenImpCasts(declRefExpr(to(
varDecl(hasType(isInteger())).bind(ConditionVarName)))));
static const DeclarationMatcher InitToZeroMatcher =
varDecl(hasInitializer(ignoringParenImpCasts(
integerLiteral(equals(0))))).bind(InitVarName);
static const StatementMatcher IncrementVarMatcher =
declRefExpr(to(
varDecl(hasType(isInteger())).bind(IncrementVarName)));
// FIXME: How best to document complicated matcher expressions? They're fairly
// self-documenting...but there may be some unintuitive parts.
/// \brief The matcher for loops over arrays.
///
/// In this general example, assuming 'j' and 'k' are of integral type:
/// \code
/// for (int i = 0; j < 3 + 2; ++k) { ... }
/// \endcode
/// The following string identifiers are bound to these parts of the AST:
/// ConditionVarName: 'j' (as a VarDecl)
/// ConditionBoundName: '3 + 2' (as an Expr)
/// InitVarName: 'i' (as a VarDecl)
/// IncrementVarName: 'k' (as a VarDecl)
/// LoopName: The entire for loop (as a ForStmt)
///
/// Client code will need to make sure that:
/// - The three index variables identified by the matcher are the same
/// VarDecl.
/// - The index variable is only used as an array index.
/// - All arrays indexed by the loop are the same.
StatementMatcher makeArrayLoopMatcher() {
StatementMatcher ArrayBoundMatcher =
expr(hasType(isInteger())).bind(ConditionBoundName);
return forStmt(
hasLoopInit(declStmt(hasSingleDecl(InitToZeroMatcher))),
hasCondition(anyOf(binaryOperator(hasOperatorName("<"),
hasLHS(IntegerComparisonMatcher),
hasRHS(ArrayBoundMatcher)),
binaryOperator(hasOperatorName(">"),
hasLHS(ArrayBoundMatcher),
hasRHS(IntegerComparisonMatcher)))),
hasIncrement(unaryOperator(hasOperatorName("++"),
hasUnaryOperand(IncrementVarMatcher))))
.bind(LoopName);
}
/// \brief The matcher used for iterator-based for loops.
///
/// This matcher is more flexible than array-based loops. It will match
/// catch loops of the following textual forms (regardless of whether the
/// iterator type is actually a pointer type or a class type):
///
/// Assuming f, g, and h are of type containerType::iterator,
/// \code
/// for (containerType::iterator it = container.begin(),
/// e = createIterator(); f != g; ++h) { ... }
/// for (containerType::iterator it = container.begin();
/// f != anotherContainer.end(); ++h) { ... }
/// \endcode
/// The following string identifiers are bound to the parts of the AST:
/// InitVarName: 'it' (as a VarDecl)
/// ConditionVarName: 'f' (as a VarDecl)
/// LoopName: The entire for loop (as a ForStmt)
/// In the first example only:
/// EndVarName: 'e' (as a VarDecl)
/// ConditionEndVarName: 'g' (as a VarDecl)
/// In the second example only:
/// EndCallName: 'container.end()' (as a CXXMemberCallExpr)
///
/// Client code will need to make sure that:
/// - The iterator variables 'it', 'f', and 'h' are the same
/// - The two containers on which 'begin' and 'end' are called are the same
/// - If the end iterator variable 'g' is defined, it is the same as 'f'
StatementMatcher makeIteratorLoopMatcher() {
StatementMatcher BeginCallMatcher =
cxxMemberCallExpr(
argumentCountIs(0),
callee(
cxxMethodDecl(hasName("begin"))
)
).bind(BeginCallName);
DeclarationMatcher InitDeclMatcher =
varDecl(
hasInitializer(
anyOf(
ignoringParenImpCasts(BeginCallMatcher),
materializeTemporaryExpr(ignoringParenImpCasts(BeginCallMatcher)),
hasDescendant(BeginCallMatcher)
)
)
).bind(InitVarName);
DeclarationMatcher EndDeclMatcher =
varDecl(hasInitializer(anything())).bind(EndVarName);
StatementMatcher EndCallMatcher = cxxMemberCallExpr(
argumentCountIs(0), callee(cxxMethodDecl(hasName("end"))));
StatementMatcher IteratorBoundMatcher =
expr(anyOf(ignoringParenImpCasts(declRefExpr(to(
varDecl().bind(ConditionEndVarName)))),
ignoringParenImpCasts(
expr(EndCallMatcher).bind(EndCallName)),
materializeTemporaryExpr(ignoringParenImpCasts(
expr(EndCallMatcher).bind(EndCallName)))));
StatementMatcher IteratorComparisonMatcher =
expr(ignoringParenImpCasts(declRefExpr(to(
varDecl().bind(ConditionVarName)))));
StatementMatcher OverloadedNEQMatcher = cxxOperatorCallExpr(
hasOverloadedOperatorName("!="),
argumentCountIs(2),
hasArgument(0, IteratorComparisonMatcher),
hasArgument(1, IteratorBoundMatcher));
// This matcher tests that a declaration is a CXXRecordDecl that has an
// overloaded operator*(). If the operator*() returns by value instead of by
// reference then the return type is tagged with DerefByValueResultName.
internal::Matcher<VarDecl> TestDerefReturnsByValue =
hasType(
cxxRecordDecl(
hasMethod(
allOf(
hasOverloadedOperatorName("*"),
anyOf(
// Tag the return type if it's by value.
returns(
qualType(
unless(hasCanonicalType(referenceType()))
).bind(DerefByValueResultName)
),
returns(
// Skip loops where the iterator's operator* returns an
// rvalue reference. This is just weird.
qualType(
unless(
hasCanonicalType(rValueReferenceType())
)
).bind(DerefByRefResultName)
)
)
)
)
)
);
return
forStmt(
hasLoopInit(anyOf(
declStmt(
declCountIs(2),
containsDeclaration(0, InitDeclMatcher),
containsDeclaration(1, EndDeclMatcher)
),
declStmt(hasSingleDecl(InitDeclMatcher))
)),
hasCondition(anyOf(
binaryOperator(
hasOperatorName("!="),
hasLHS(IteratorComparisonMatcher),
hasRHS(IteratorBoundMatcher)
),
binaryOperator(
hasOperatorName("!="),
hasLHS(IteratorBoundMatcher),
hasRHS(IteratorComparisonMatcher)
),
OverloadedNEQMatcher
)),
hasIncrement(anyOf(
unaryOperator(
hasOperatorName("++"),
hasUnaryOperand(
declRefExpr(to(
varDecl(hasType(pointsTo(AnyType))).bind(IncrementVarName)
))
)
),
cxxOperatorCallExpr(
hasOverloadedOperatorName("++"),
hasArgument(0,
declRefExpr(to(
varDecl(TestDerefReturnsByValue).bind(IncrementVarName)
))
)
)
))
).bind(LoopName);
}
/// \brief The matcher used for array-like containers (pseudoarrays).
///
/// This matcher is more flexible than array-based loops. It will match
/// loops of the following textual forms (regardless of whether the
/// iterator type is actually a pointer type or a class type):
///
/// Assuming f, g, and h are of type containerType::iterator,
/// \code
/// for (int i = 0, j = container.size(); f < g; ++h) { ... }
/// for (int i = 0; f < container.size(); ++h) { ... }
/// \endcode
/// The following string identifiers are bound to the parts of the AST:
/// InitVarName: 'i' (as a VarDecl)
/// ConditionVarName: 'f' (as a VarDecl)
/// LoopName: The entire for loop (as a ForStmt)
/// In the first example only:
/// EndVarName: 'j' (as a VarDecl)
/// ConditionEndVarName: 'g' (as a VarDecl)
/// In the second example only:
/// EndCallName: 'container.size()' (as a CXXMemberCallExpr)
///
/// Client code will need to make sure that:
/// - The index variables 'i', 'f', and 'h' are the same
/// - The containers on which 'size()' is called is the container indexed
/// - The index variable is only used in overloaded operator[] or
/// container.at()
/// - If the end iterator variable 'g' is defined, it is the same as 'j'
/// - The container's iterators would not be invalidated during the loop
StatementMatcher makePseudoArrayLoopMatcher() {
// Test that the incoming type has a record declaration that has methods
// called 'begin' and 'end'. If the incoming type is const, then make sure
// these methods are also marked const.
//
// FIXME: To be completely thorough this matcher should also ensure the
// return type of begin/end is an iterator that dereferences to the same as
// what operator[] or at() returns. Such a test isn't likely to fail except
// for pathological cases.
//
// FIXME: Also, a record doesn't necessarily need begin() and end(). Free
// functions called begin() and end() taking the container as an argument
// are also allowed.
TypeMatcher RecordWithBeginEnd =
qualType(anyOf(
qualType(
isConstQualified(),
hasDeclaration(
cxxRecordDecl(
hasMethod(
cxxMethodDecl(
hasName("begin"),
isConst()
)
),
hasMethod(
cxxMethodDecl(
hasName("end"),
isConst()
)
)
)
) // hasDeclaration
), // qualType
qualType(
unless(isConstQualified()),
hasDeclaration(
cxxRecordDecl(
hasMethod(hasName("begin")),
hasMethod(hasName("end"))
)
)
) // qualType
)
);
StatementMatcher SizeCallMatcher = cxxMemberCallExpr(
argumentCountIs(0),
callee(cxxMethodDecl(anyOf(hasName("size"), hasName("length")))),
on(anyOf(hasType(pointsTo(RecordWithBeginEnd)),
hasType(RecordWithBeginEnd))));
StatementMatcher EndInitMatcher =
expr(anyOf(
ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)),
explicitCastExpr(hasSourceExpression(ignoringParenImpCasts(
expr(SizeCallMatcher).bind(EndCallName))))));
DeclarationMatcher EndDeclMatcher =
varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName);
StatementMatcher IndexBoundMatcher =
expr(anyOf(
ignoringParenImpCasts(declRefExpr(to(
varDecl(hasType(isInteger())).bind(ConditionEndVarName)))),
EndInitMatcher));
return forStmt(
hasLoopInit(anyOf(
declStmt(declCountIs(2),
containsDeclaration(0, InitToZeroMatcher),
containsDeclaration(1, EndDeclMatcher)),
declStmt(hasSingleDecl(InitToZeroMatcher)))),
hasCondition(anyOf(
binaryOperator(hasOperatorName("<"),
hasLHS(IntegerComparisonMatcher),
hasRHS(IndexBoundMatcher)),
binaryOperator(hasOperatorName(">"),
hasLHS(IndexBoundMatcher),
hasRHS(IntegerComparisonMatcher)))),
hasIncrement(unaryOperator(
hasOperatorName("++"),
hasUnaryOperand(IncrementVarMatcher))))
.bind(LoopName);
}

View File

@ -1,42 +0,0 @@
//===-- LoopConvert/LoopMatchers.h - Matchers for for loops -----*- 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 contains declarations of the matchers for use in migrating
/// C++ for loops. The matchers are responsible for checking the general shape
/// of the for loop, namely the init, condition, and increment portions.
/// Further analysis will be needed to confirm that the loop is in fact
/// convertible in the matcher callback.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_LOOP_MATCHERS_H
#define CLANG_MODERNIZE_LOOP_MATCHERS_H
#include "clang/ASTMatchers/ASTMatchers.h"
// Constants used for matcher name bindings
extern const char LoopName[];
extern const char ConditionBoundName[];
extern const char ConditionVarName[];
extern const char ConditionEndVarName[];
extern const char IncrementVarName[];
extern const char InitVarName[];
extern const char BeginCallName[];
extern const char EndExprName[];
extern const char EndCallName[];
extern const char EndVarName[];
extern const char DerefByValueResultName[];
extern const char DerefByRefResultName[];
clang::ast_matchers::StatementMatcher makeArrayLoopMatcher();
clang::ast_matchers::StatementMatcher makeIteratorLoopMatcher();
clang::ast_matchers::StatementMatcher makePseudoArrayLoopMatcher();
#endif // CLANG_MODERNIZE_LOOP_MATCHERS_H

View File

@ -1,140 +0,0 @@
//===-- LoopConvert/StmtAncestor.cpp - AST property visitors --------------===//
//
// 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 contains the definitions of several RecursiveASTVisitors
/// used to build and check data structures used in loop migration.
///
//===----------------------------------------------------------------------===//
#include "StmtAncestor.h"
using namespace clang;
/// \brief Tracks a stack of parent statements during traversal.
///
/// All this really does is inject push_back() before running
/// RecursiveASTVisitor::TraverseStmt() and pop_back() afterwards. The Stmt atop
/// the stack is the parent of the current statement (NULL for the topmost
/// statement).
bool StmtAncestorASTVisitor::TraverseStmt(Stmt *Statement) {
StmtAncestors.insert(std::make_pair(Statement, StmtStack.back()));
StmtStack.push_back(Statement);
RecursiveASTVisitor<StmtAncestorASTVisitor>::TraverseStmt(Statement);
StmtStack.pop_back();
return true;
}
/// \brief Keep track of the DeclStmt associated with each VarDecl.
///
/// Combined with StmtAncestors, this provides roughly the same information as
/// Scope, as we can map a VarDecl to its DeclStmt, then walk up the parent tree
/// using StmtAncestors.
bool StmtAncestorASTVisitor::VisitDeclStmt(DeclStmt *Decls) {
for (DeclStmt::const_decl_iterator I = Decls->decl_begin(),
E = Decls->decl_end(); I != E; ++I)
if (const VarDecl *V = dyn_cast<VarDecl>(*I))
DeclParents.insert(std::make_pair(V, Decls));
return true;
}
/// \brief record the DeclRefExpr as part of the parent expression.
bool ComponentFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
Components.push_back(E);
return true;
}
/// \brief record the MemberExpr as part of the parent expression.
bool ComponentFinderASTVisitor::VisitMemberExpr(MemberExpr *Member) {
Components.push_back(Member);
return true;
}
/// \brief Forward any DeclRefExprs to a check on the referenced variable
/// declaration.
bool DependencyFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
if (VarDecl *V = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
return VisitVarDecl(V);
return true;
}
/// \brief Determine if any this variable is declared inside the ContainingStmt.
bool DependencyFinderASTVisitor::VisitVarDecl(VarDecl *V) {
const Stmt *Curr = DeclParents->lookup(V);
// First, see if the variable was declared within an inner scope of the loop.
while (Curr != nullptr) {
if (Curr == ContainingStmt) {
DependsOnInsideVariable = true;
return false;
}
Curr = StmtParents->lookup(Curr);
}
// Next, check if the variable was removed from existence by an earlier
// iteration.
for (ReplacedVarsMap::const_iterator I = ReplacedVars->begin(),
E = ReplacedVars->end(); I != E; ++I)
if ((*I).second == V) {
DependsOnInsideVariable = true;
return false;
}
return true;
}
/// \brief If we already created a variable for TheLoop, check to make sure
/// that the name was not already taken.
bool DeclFinderASTVisitor::VisitForStmt(ForStmt *TheLoop) {
StmtGeneratedVarNameMap::const_iterator I = GeneratedDecls->find(TheLoop);
if (I != GeneratedDecls->end() && I->second == Name) {
Found = true;
return false;
}
return true;
}
/// \brief If any named declaration within the AST subtree has the same name,
/// then consider Name already taken.
bool DeclFinderASTVisitor::VisitNamedDecl(NamedDecl *D) {
const IdentifierInfo *Ident = D->getIdentifier();
if (Ident && Ident->getName() == Name) {
Found = true;
return false;
}
return true;
}
/// \brief Forward any declaration references to the actual check on the
/// referenced declaration.
bool DeclFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
if (NamedDecl *D = dyn_cast<NamedDecl>(DeclRef->getDecl()))
return VisitNamedDecl(D);
return true;
}
/// \brief If the new variable name conflicts with any type used in the loop,
/// then we mark that variable name as taken.
bool DeclFinderASTVisitor::VisitTypeLoc(TypeLoc TL) {
QualType QType = TL.getType();
// Check if our name conflicts with a type, to handle for typedefs.
if (QType.getAsString() == Name) {
Found = true;
return false;
}
// Check for base type conflicts. For example, when a struct is being
// referenced in the body of the loop, the above getAsString() will return the
// whole type (ex. "struct s"), but will be caught here.
if (const IdentifierInfo *Ident = QType.getBaseTypeIdentifier()) {
if (Ident->getName() == Name) {
Found = true;
return false;
}
}
return true;
}

View File

@ -1,201 +0,0 @@
//===-- LoopConvert/StmtAncestor.h - AST property visitors ------*- 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 contains the declarations of several RecursiveASTVisitors
/// used to build and check data structures used in loop migration.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_STMT_ANCESTOR_H
#define CLANG_MODERNIZE_STMT_ANCESTOR_H
#include "clang/AST/RecursiveASTVisitor.h"
/// A map used to walk the AST in reverse: maps child Stmt to parent Stmt.
typedef llvm::DenseMap<const clang::Stmt*, const clang::Stmt*> StmtParentMap;
/// A map used to walk the AST in reverse:
/// maps VarDecl to the to parent DeclStmt.
typedef
llvm::DenseMap<const clang::VarDecl*, const clang::DeclStmt*> DeclParentMap;
/// A map used to track which variables have been removed by a refactoring pass.
/// It maps the parent ForStmt to the removed index variable's VarDecl.
typedef
llvm::DenseMap<const clang::ForStmt*, const clang::VarDecl*> ReplacedVarsMap;
/// A map used to remember the variable names generated in a Stmt
typedef llvm::DenseMap<const clang::Stmt*, std::string> StmtGeneratedVarNameMap;
/// A vector used to store the AST subtrees of an Expr.
typedef llvm::SmallVector<const clang::Expr*, 16> ComponentVector;
/// \brief Class used build the reverse AST properties needed to detect
/// name conflicts and free variables.
class StmtAncestorASTVisitor :
public clang::RecursiveASTVisitor<StmtAncestorASTVisitor> {
public:
StmtAncestorASTVisitor() {
StmtStack.push_back(nullptr);
}
/// \brief Run the analysis on the TranslationUnitDecl.
///
/// In case we're running this analysis multiple times, don't repeat the work.
void gatherAncestors(const clang::TranslationUnitDecl *T) {
if (StmtAncestors.empty())
TraverseDecl(const_cast<clang::TranslationUnitDecl*>(T));
}
/// Accessor for StmtAncestors.
const StmtParentMap &getStmtToParentStmtMap() {
return StmtAncestors;
}
/// Accessor for DeclParents.
const DeclParentMap &getDeclToParentStmtMap() {
return DeclParents;
}
friend class clang::RecursiveASTVisitor<StmtAncestorASTVisitor>;
private:
StmtParentMap StmtAncestors;
DeclParentMap DeclParents;
llvm::SmallVector<const clang::Stmt*, 16> StmtStack;
bool TraverseStmt(clang::Stmt *Statement);
bool VisitDeclStmt(clang::DeclStmt *Statement);
};
/// Class used to find the variables and member expressions on which an
/// arbitrary expression depends.
class ComponentFinderASTVisitor :
public clang::RecursiveASTVisitor<ComponentFinderASTVisitor> {
public:
ComponentFinderASTVisitor() { }
/// Find the components of an expression and place them in a ComponentVector.
void findExprComponents(const clang::Expr *SourceExpr) {
clang::Expr *E = const_cast<clang::Expr *>(SourceExpr);
TraverseStmt(E);
}
/// Accessor for Components.
const ComponentVector &getComponents() {
return Components;
}
friend class clang::RecursiveASTVisitor<ComponentFinderASTVisitor>;
private:
ComponentVector Components;
bool VisitDeclRefExpr(clang::DeclRefExpr *E);
bool VisitMemberExpr(clang::MemberExpr *Member);
};
/// Class used to determine if an expression is dependent on a variable declared
/// inside of the loop where it would be used.
class DependencyFinderASTVisitor :
public clang::RecursiveASTVisitor<DependencyFinderASTVisitor> {
public:
DependencyFinderASTVisitor(const StmtParentMap *StmtParents,
const DeclParentMap *DeclParents,
const ReplacedVarsMap *ReplacedVars,
const clang::Stmt *ContainingStmt) :
StmtParents(StmtParents), DeclParents(DeclParents),
ContainingStmt(ContainingStmt), ReplacedVars(ReplacedVars) { }
/// \brief Run the analysis on Body, and return true iff the expression
/// depends on some variable declared within ContainingStmt.
///
/// This is intended to protect against hoisting the container expression
/// outside of an inner context if part of that expression is declared in that
/// inner context.
///
/// For example,
/// \code
/// const int N = 10, M = 20;
/// int arr[N][M];
/// int getRow();
///
/// for (int i = 0; i < M; ++i) {
/// int k = getRow();
/// printf("%d:", arr[k][i]);
/// }
/// \endcode
/// At first glance, this loop looks like it could be changed to
/// \code
/// for (int elem : arr[k]) {
/// int k = getIndex();
/// printf("%d:", elem);
/// }
/// \endcode
/// But this is malformed, since `k` is used before it is defined!
///
/// In order to avoid this, this class looks at the container expression
/// `arr[k]` and decides whether or not it contains a sub-expression declared
/// within the the loop body.
bool dependsOnInsideVariable(const clang::Stmt *Body) {
DependsOnInsideVariable = false;
TraverseStmt(const_cast<clang::Stmt *>(Body));
return DependsOnInsideVariable;
}
friend class clang::RecursiveASTVisitor<DependencyFinderASTVisitor>;
private:
const StmtParentMap *StmtParents;
const DeclParentMap *DeclParents;
const clang::Stmt *ContainingStmt;
const ReplacedVarsMap *ReplacedVars;
bool DependsOnInsideVariable;
bool VisitVarDecl(clang::VarDecl *V);
bool VisitDeclRefExpr(clang::DeclRefExpr *D);
};
/// Class used to determine if any declarations used in a Stmt would conflict
/// with a particular identifier. This search includes the names that don't
/// actually appear in the AST (i.e. created by a refactoring tool) by including
/// a map from Stmts to generated names associated with those stmts.
class DeclFinderASTVisitor :
public clang::RecursiveASTVisitor<DeclFinderASTVisitor> {
public:
DeclFinderASTVisitor(const std::string &Name,
const StmtGeneratedVarNameMap *GeneratedDecls) :
Name(Name), GeneratedDecls(GeneratedDecls), Found(false) { }
/// Attempts to find any usages of variables name Name in Body, returning
/// true when it is used in Body. This includes the generated loop variables
/// of ForStmts which have already been transformed.
bool findUsages(const clang::Stmt *Body) {
Found = false;
TraverseStmt(const_cast<clang::Stmt *>(Body));
return Found;
}
friend class clang::RecursiveASTVisitor<DeclFinderASTVisitor>;
private:
std::string Name;
/// GeneratedDecls keeps track of ForStmts which have been transformed,
/// mapping each modified ForStmt to the variable generated in the loop.
const StmtGeneratedVarNameMap *GeneratedDecls;
bool Found;
bool VisitForStmt(clang::ForStmt *F);
bool VisitNamedDecl(clang::NamedDecl *D);
bool VisitDeclRefExpr(clang::DeclRefExpr *D);
bool VisitTypeLoc(clang::TypeLoc TL);
};
#endif // CLANG_MODERNIZE_STMT_ANCESTOR_H

View File

@ -1,95 +0,0 @@
//===-- LoopConvert/VariableNaming.cpp - Gererate variable names ----------===//
//
// 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 contains the definitino of the VariableNamer class, which
/// is responsible for generating new variable names and ensuring that they do
/// not conflict with existing ones.
///
//===----------------------------------------------------------------------===//
#include "VariableNaming.h"
using namespace llvm;
using namespace clang;
std::string VariableNamer::createIndexName() {
// FIXME: Add in naming conventions to handle:
// - Uppercase/lowercase indices
// - How to handle conflicts
// - An interactive process for naming
std::string IteratorName;
std::string ContainerName;
if (TheContainer)
ContainerName = TheContainer->getName().str();
size_t Len = ContainerName.length();
if (Len > 1 && ContainerName[Len - 1] == 's')
IteratorName = ContainerName.substr(0, Len - 1);
else
IteratorName = "elem";
if (!declarationExists(IteratorName))
return IteratorName;
IteratorName = ContainerName + "_" + OldIndex->getName().str();
if (!declarationExists(IteratorName))
return IteratorName;
IteratorName = ContainerName + "_elem";
if (!declarationExists(IteratorName))
return IteratorName;
IteratorName += "_elem";
if (!declarationExists(IteratorName))
return IteratorName;
IteratorName = "_elem_";
// Someone defeated my naming scheme...
while (declarationExists(IteratorName))
IteratorName += "i";
return IteratorName;
}
/// \brief Determines whether or not the the name \a Symbol conflicts with
/// language keywords or defined macros. Also checks if the name exists in
/// LoopContext, any of its parent contexts, or any of its child statements.
///
/// We also check to see if the same identifier was generated by this loop
/// converter in a loop nested within SourceStmt.
bool VariableNamer::declarationExists(StringRef Symbol) {
assert(Context != nullptr && "Expected an ASTContext");
IdentifierInfo &Ident = Context->Idents.get(Symbol);
// Check if the symbol is not an identifier (ie. is a keyword or alias).
if (!isAnyIdentifier(Ident.getTokenID()))
return true;
// Check for conflicting macro definitions.
if (Ident.hasMacroDefinition())
return true;
// Determine if the symbol was generated in a parent context.
for (const Stmt *S = SourceStmt; S != nullptr; S = ReverseAST->lookup(S)) {
StmtGeneratedVarNameMap::const_iterator I = GeneratedDecls->find(S);
if (I != GeneratedDecls->end() && I->second == Symbol)
return true;
}
// FIXME: Rather than detecting conflicts at their usages, we should check the
// parent context.
// For some reason, lookup() always returns the pair (NULL, NULL) because its
// StoredDeclsMap is not initialized (i.e. LookupPtr.getInt() is false inside
// of DeclContext::lookup()). Why is this?
// Finally, determine if the symbol was used in the loop or a child context.
DeclFinderASTVisitor DeclFinder(Symbol, GeneratedDecls);
return DeclFinder.findUsages(SourceStmt);
}

View File

@ -1,59 +0,0 @@
//===-- LoopConvert/VariableNaming.h - Gererate variable names --*- 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 contains the declaration of the VariableNamer class, which
/// is responsible for generating new variable names and ensuring that they do
/// not conflict with existing ones.
//
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_VARIABLE_NAMING_H
#define CLANG_MODERNIZE_VARIABLE_NAMING_H
#include "StmtAncestor.h"
#include "clang/AST/ASTContext.h"
/// \brief Create names for generated variables within a particular statement.
///
/// VariableNamer uses a DeclContext as a reference point, checking for any
/// conflicting declarations higher up in the context or within SourceStmt.
/// It creates a variable name using hints from a source container and the old
/// index, if they exist.
class VariableNamer {
public:
VariableNamer(
StmtGeneratedVarNameMap *GeneratedDecls, const StmtParentMap *ReverseAST,
const clang::Stmt *SourceStmt, const clang::VarDecl *OldIndex,
const clang::VarDecl *TheContainer, const clang::ASTContext *Context)
: GeneratedDecls(GeneratedDecls), ReverseAST(ReverseAST),
SourceStmt(SourceStmt), OldIndex(OldIndex), TheContainer(TheContainer),
Context(Context) {}
/// \brief Generate a new index name.
///
/// Generates the name to be used for an inserted iterator. It relies on
/// declarationExists() to determine that there are no naming conflicts, and
/// tries to use some hints from the container name and the old index name.
std::string createIndexName();
private:
StmtGeneratedVarNameMap *GeneratedDecls;
const StmtParentMap *ReverseAST;
const clang::Stmt *SourceStmt;
const clang::VarDecl *OldIndex;
const clang::VarDecl *TheContainer;
const clang::ASTContext *Context;
// Determine whether or not a declaration that would conflict with Symbol
// exists in an outer context or in any statement contained in SourceStmt.
bool declarationExists(llvm::StringRef Symbol);
};
#endif // CLANG_MODERNIZE_VARIABLE_NAMING_H

View File

@ -1,15 +0,0 @@
##===- tools/extra/loop-convert/Makefile ----sssss----------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
CLANG_LEVEL := ../../..
include $(CLANG_LEVEL)/../../Makefile.config
DIRS = Core tool
include $(CLANG_LEVEL)/Makefile

View File

@ -1,78 +0,0 @@
//===-- PassByValue.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 the implementation of the ReplaceAutoPtrTransform
/// class.
///
//===----------------------------------------------------------------------===//
#include "PassByValue.h"
#include "PassByValueActions.h"
#include "PassByValueMatchers.h"
using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;
int PassByValueTransform::apply(const tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool Tool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
unsigned RejectedChanges = 0;
MatchFinder Finder;
ConstructorParamReplacer Replacer(AcceptedChanges, RejectedChanges,
/*Owner=*/ *this);
Finder.addMatcher(makePassByValueCtorParamMatcher(), &Replacer);
// make the replacer available to handleBeginSource()
this->Replacer = &Replacer;
if (Tool.run(createActionFactory(Finder).get())) {
llvm::errs() << "Error encountered during translation.\n";
return 1;
}
setAcceptedChanges(AcceptedChanges);
setRejectedChanges(RejectedChanges);
return 0;
}
bool PassByValueTransform::handleBeginSource(CompilerInstance &CI,
llvm::StringRef Filename) {
assert(Replacer && "Replacer not set");
IncludeManager.reset(new IncludeDirectives(CI));
Replacer->setIncludeDirectives(IncludeManager.get());
return Transform::handleBeginSource(CI, Filename);
}
namespace {
struct PassByValueFactory : TransformFactory {
PassByValueFactory() {
// Based on the Replace Auto-Ptr Transform that is also using std::move().
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 6);
Since.Icc = Version(13);
Since.Msvc = Version(11);
}
Transform *createTransform(const TransformOptions &Opts) override {
return new PassByValueTransform(Opts);
}
};
} // namespace
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<PassByValueFactory>
X("pass-by-value", "Pass parameters by value where possible");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int PassByValueTransformAnchorSource = 0;

View File

@ -1,73 +0,0 @@
//===-- PassByValue.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 the declaration of the PassByValueTransform
/// class.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_PASS_BY_VALUE_H
#define CLANG_MODERNIZE_PASS_BY_VALUE_H
#include "Core/IncludeDirectives.h"
#include "Core/Transform.h"
class ConstructorParamReplacer;
/// \brief Subclass of Transform that uses pass-by-value semantic when move
/// constructors are available to avoid copies.
///
/// When a class constructor accepts an object by const reference with the
/// intention of copying the object the copy can be avoided in certain
/// situations if the object has a move constructor. First, the constructor is
/// changed to accept the object by value instead. Then this argument is moved
/// instead of copied into class-local storage. If an l-value is provided to the
/// constructor, there is no difference in the number of copies made. However,
/// if an r-value is passed, the copy is avoided completely.
///
/// For example, given:
/// \code
/// #include <string>
///
/// class A {
/// std::string S;
/// public:
/// A(const std::string &S) : S(S) {}
/// };
/// \endcode
/// the code is transformed to:
/// \code
/// #include <string>
///
/// class A {
/// std::string S;
/// public:
/// A(std::string S) : S(std::move(S)) {}
/// };
/// \endcode
class PassByValueTransform : public Transform {
public:
PassByValueTransform(const TransformOptions &Options)
: Transform("PassByValue", Options), Replacer(nullptr) {}
/// \see Transform::apply().
int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) override;
private:
/// \brief Setups the \c IncludeDirectives for the replacer.
bool handleBeginSource(clang::CompilerInstance &CI,
llvm::StringRef Filename) override;
std::unique_ptr<IncludeDirectives> IncludeManager;
ConstructorParamReplacer *Replacer;
};
#endif // CLANG_MODERNIZE_PASS_BY_VALUE_H

View File

@ -1,175 +0,0 @@
//===-- PassByValueActions.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 contains the definition of the ASTMatcher callback for the
/// PassByValue transform.
///
//===----------------------------------------------------------------------===//
#include "PassByValueActions.h"
#include "Core/IncludeDirectives.h"
#include "Core/Transform.h"
#include "PassByValueMatchers.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;
namespace {
/// \brief \c clang::RecursiveASTVisitor that checks that the given
/// \c ParmVarDecl is used exactly one time.
///
/// \see ExactlyOneUsageVisitor::hasExactlyOneUsageIn()
class ExactlyOneUsageVisitor
: public RecursiveASTVisitor<ExactlyOneUsageVisitor> {
friend class RecursiveASTVisitor<ExactlyOneUsageVisitor>;
public:
ExactlyOneUsageVisitor(const ParmVarDecl *ParamDecl) : ParamDecl(ParamDecl) {}
/// \brief Whether or not the parameter variable is referred only once in the
/// given constructor.
bool hasExactlyOneUsageIn(const CXXConstructorDecl *Ctor) {
Count = 0;
TraverseDecl(const_cast<CXXConstructorDecl *>(Ctor));
return Count == 1;
}
private:
/// \brief Counts the number of references to a variable.
///
/// Stops the AST traversal if more than one usage is found.
bool VisitDeclRefExpr(DeclRefExpr *D) {
if (const ParmVarDecl *To = llvm::dyn_cast<ParmVarDecl>(D->getDecl()))
if (To == ParamDecl) {
++Count;
if (Count > 1)
// no need to look further, used more than once
return false;
}
return true;
}
const ParmVarDecl *ParamDecl;
unsigned Count;
};
} // end anonymous namespace
/// \brief Whether or not \p ParamDecl is used exactly one time in \p Ctor.
///
/// Checks both in the init-list and the body of the constructor.
static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor,
const ParmVarDecl *ParamDecl) {
ExactlyOneUsageVisitor Visitor(ParamDecl);
return Visitor.hasExactlyOneUsageIn(Ctor);
}
/// \brief Find all references to \p ParamDecl across all of the
/// redeclarations of \p Ctor.
static void
collectParamDecls(const CXXConstructorDecl *Ctor, const ParmVarDecl *ParamDecl,
llvm::SmallVectorImpl<const ParmVarDecl *> &Results) {
unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
for (CXXConstructorDecl::redecl_iterator I = Ctor->redecls_begin(),
E = Ctor->redecls_end();
I != E; ++I)
Results.push_back((*I)->getParamDecl(ParamIdx));
}
void ConstructorParamReplacer::run(const MatchFinder::MatchResult &Result) {
assert(IncludeManager && "Include directives manager not set.");
SourceManager &SM = *Result.SourceManager;
const CXXConstructorDecl *Ctor =
Result.Nodes.getNodeAs<CXXConstructorDecl>(PassByValueCtorId);
const ParmVarDecl *ParamDecl =
Result.Nodes.getNodeAs<ParmVarDecl>(PassByValueParamId);
const CXXCtorInitializer *Initializer =
Result.Nodes.getNodeAs<CXXCtorInitializer>(PassByValueInitializerId);
assert(Ctor && ParamDecl && Initializer && "Bad Callback, missing node.");
// Check this now to avoid unnecessary work. The param locations are checked
// later.
if (!Owner.isFileModifiable(SM, Initializer->getSourceLocation()))
return;
// The parameter will be in an unspecified state after the move, so check if
// the parameter is used for anything else other than the copy. If so do not
// apply any changes.
if (!paramReferredExactlyOnce(Ctor, ParamDecl))
return;
llvm::SmallVector<const ParmVarDecl *, 2> AllParamDecls;
collectParamDecls(Ctor, ParamDecl, AllParamDecls);
// Generate all replacements for the params.
llvm::SmallVector<Replacement, 2> ParamReplaces;
for (unsigned I = 0, E = AllParamDecls.size(); I != E; ++I) {
TypeLoc ParamTL = AllParamDecls[I]->getTypeSourceInfo()->getTypeLoc();
ReferenceTypeLoc RefTL = ParamTL.getAs<ReferenceTypeLoc>();
SourceRange Range(AllParamDecls[I]->getLocStart(), ParamTL.getLocEnd());
CharSourceRange CharRange = Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(Range), SM, LangOptions());
// do not generate a replacement when the parameter is already a value
if (RefTL.isNull())
continue;
// transform non-value parameters (e.g: const-ref) to values
TypeLoc ValueTypeLoc = RefTL.getPointeeLoc();
llvm::SmallString<32> ValueStr = Lexer::getSourceText(
CharSourceRange::getTokenRange(ValueTypeLoc.getSourceRange()), SM,
LangOptions());
// If it's impossible to change one of the parameter (e.g: comes from an
// unmodifiable header) quit the callback now, do not generate any changes.
if (CharRange.isInvalid() || ValueStr.empty() ||
!Owner.isFileModifiable(SM, CharRange.getBegin()))
return;
// 'const Foo &param' -> 'Foo param'
// ~~~~~~~~~~~ ~~~^
ValueStr += ' ';
ParamReplaces.push_back(Replacement(SM, CharRange, ValueStr));
}
// Reject the changes if the the risk level is not acceptable.
if (!Owner.isAcceptableRiskLevel(RL_Reasonable)) {
RejectedChanges++;
return;
}
// if needed, include <utility> in the file that uses std::move()
const FileEntry *STDMoveFile =
SM.getFileEntryForID(SM.getFileID(Initializer->getLParenLoc()));
const tooling::Replacement &IncludeReplace =
IncludeManager->addAngledInclude(STDMoveFile, "utility");
if (IncludeReplace.isApplicable()) {
Owner.addReplacementForCurrentTU(IncludeReplace);
AcceptedChanges++;
}
// const-ref params becomes values (const Foo & -> Foo)
for (const Replacement *I = ParamReplaces.begin(), *E = ParamReplaces.end();
I != E; ++I) {
Owner.addReplacementForCurrentTU(*I);
}
AcceptedChanges += ParamReplaces.size();
// move the value in the init-list
Owner.addReplacementForCurrentTU(Replacement(
SM, Initializer->getLParenLoc().getLocWithOffset(1), 0, "std::move("));
Owner.addReplacementForCurrentTU(
Replacement(SM, Initializer->getRParenLoc(), 0, ")"));
AcceptedChanges += 2;
}

View File

@ -1,74 +0,0 @@
//===-- PassByValueActions.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 contains the declaration of the ASTMatcher callback for the
/// PassByValue transform.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_PASS_BY_VALUE_ACTIONS_H
#define CLANG_MODERNIZE_PASS_BY_VALUE_ACTIONS_H
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Refactoring.h"
class Transform;
class IncludeDirectives;
/// \brief Callback that replaces const-ref parameters in constructors to use
/// pass-by-value semantic where applicable.
///
/// Modifications done by the callback:
/// - \#include \<utility\> is added if necessary for the definition of
/// \c std::move() to be available.
/// - The parameter type is changed from const-ref to value-type.
/// - In the init-list the parameter is moved.
///
/// Example:
/// \code
/// + #include <utility>
///
/// class Foo(const std::string &S) {
/// public:
/// - Foo(const std::string &S) : S(S) {}
/// + Foo(std::string S) : S(std::move(S)) {}
///
/// private:
/// std::string S;
/// };
/// \endcode
///
/// \note Since an include may be added by this matcher it's necessary to call
/// \c setIncludeDirectives() with an up-to-date \c IncludeDirectives. This is
/// typically done by overloading \c Transform::handleBeginSource().
class ConstructorParamReplacer
: public clang::ast_matchers::MatchFinder::MatchCallback {
public:
ConstructorParamReplacer(unsigned &AcceptedChanges, unsigned &RejectedChanges,
Transform &Owner)
: AcceptedChanges(AcceptedChanges), RejectedChanges(RejectedChanges),
Owner(Owner), IncludeManager(nullptr) {}
void setIncludeDirectives(IncludeDirectives *Includes) {
IncludeManager = Includes;
}
private:
/// \brief Entry point to the callback called when matches are made.
void
run(const clang::ast_matchers::MatchFinder::MatchResult &Result) override;
unsigned &AcceptedChanges;
unsigned &RejectedChanges;
Transform &Owner;
IncludeDirectives *IncludeManager;
};
#endif // CLANG_MODERNIZE_PASS_BY_VALUE_ACTIONS_H

View File

@ -1,81 +0,0 @@
//===-- PassByValueMatchers.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 contains the definitions for matcher-generating functions
/// and names for bound nodes found by AST matchers.
///
//===----------------------------------------------------------------------===//
#include "PassByValueMatchers.h"
const char *PassByValueCtorId = "Ctor";
const char *PassByValueParamId = "Param";
const char *PassByValueInitializerId = "Initializer";
namespace clang {
namespace ast_matchers {
/// \brief Matches move constructible classes.
///
/// Given
/// \code
/// // POD types are trivially move constructible
/// struct Foo { int a; };
///
/// struct Bar {
/// Bar(Bar &&) = deleted;
/// int a;
/// };
/// \endcode
/// recordDecl(isMoveConstructible())
/// matches "Foo".
AST_MATCHER(CXXRecordDecl, isMoveConstructible) {
for (CXXRecordDecl::ctor_iterator I = Node.ctor_begin(), E = Node.ctor_end(); I != E; ++I) {
const CXXConstructorDecl *Ctor = *I;
if (Ctor->isMoveConstructor() && !Ctor->isDeleted())
return true;
}
return false;
}
} // namespace ast_matchers
} // namespace clang
using namespace clang;
using namespace clang::ast_matchers;
static TypeMatcher constRefType() {
return lValueReferenceType(pointee(isConstQualified()));
}
static TypeMatcher nonConstValueType() {
return qualType(unless(anyOf(referenceType(), isConstQualified())));
}
DeclarationMatcher makePassByValueCtorParamMatcher() {
return cxxConstructorDecl(
forEachConstructorInitializer(cxxCtorInitializer(
// Clang builds a CXXConstructExpr only when it knowns which
// constructor will be called. In dependent contexts a ParenListExpr
// is generated instead of a CXXConstructExpr, filtering out templates
// automatically for us.
withInitializer(cxxConstructExpr(
has(declRefExpr(to(
parmVarDecl(hasType(qualType(
// match only const-ref or a non-const value
// parameters, rvalues and const-values
// shouldn't be modified.
anyOf(constRefType(), nonConstValueType()))))
.bind(PassByValueParamId)))),
hasDeclaration(cxxConstructorDecl(
isCopyConstructor(), unless(isDeleted()),
hasDeclContext(cxxRecordDecl(isMoveConstructible())))))))
.bind(PassByValueInitializerId)))
.bind(PassByValueCtorId);
}

View File

@ -1,44 +0,0 @@
//===-- PassByValueMatchers.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 contains the declarations for matcher-generating functions
/// and names for bound nodes found by AST matchers.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H
#define CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H
#include "clang/ASTMatchers/ASTMatchers.h"
/// \name Names to bind with matched expressions
/// @{
extern const char *PassByValueCtorId;
extern const char *PassByValueParamId;
extern const char *PassByValueInitializerId;
/// @}
/// \brief Creates a matcher that finds class field initializations that can
/// benefit from using the move constructor.
///
/// \code
/// class A {
/// public:
/// A(const std::string &S) : S(S) {}
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PassByValueCtorId
/// ~~~~~~~~~~~~~~~~~~~~ PassByValueParamId
/// ~ PassByValueInitializerId
/// private:
/// std::string S;
/// };
/// \endcode
clang::ast_matchers::DeclarationMatcher makePassByValueCtorParamMatcher();
#endif // CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H

View File

@ -1,67 +0,0 @@
//===-- ReplaceAutoPtr.cpp ---------- std::auto_ptr replacement -----------===//
//
// 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 ReplaceAutoPtrTransform
/// class.
///
//===----------------------------------------------------------------------===//
#include "ReplaceAutoPtr.h"
#include "ReplaceAutoPtrActions.h"
#include "ReplaceAutoPtrMatchers.h"
using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;
int
ReplaceAutoPtrTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool Tool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
MatchFinder Finder;
AutoPtrReplacer Replacer(AcceptedChanges, /*Owner=*/ *this);
OwnershipTransferFixer Fixer(AcceptedChanges, /*Owner=*/ *this);
Finder.addMatcher(makeAutoPtrTypeLocMatcher(), &Replacer);
Finder.addMatcher(makeAutoPtrUsingDeclMatcher(), &Replacer);
Finder.addMatcher(makeTransferOwnershipExprMatcher(), &Fixer);
if (Tool.run(createActionFactory(Finder).get())) {
llvm::errs() << "Error encountered during translation.\n";
return 1;
}
setAcceptedChanges(AcceptedChanges);
return 0;
}
struct ReplaceAutoPtrFactory : TransformFactory {
ReplaceAutoPtrFactory() {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 6);
Since.Icc = Version(13);
Since.Msvc = Version(11);
}
Transform *createTransform(const TransformOptions &Opts) override {
return new ReplaceAutoPtrTransform(Opts);
}
};
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<ReplaceAutoPtrFactory>
X("replace-auto_ptr", "Replace std::auto_ptr (deprecated) by std::unique_ptr"
" (EXPERIMENTAL)");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int ReplaceAutoPtrTransformAnchorSource = 0;

View File

@ -1,54 +0,0 @@
//===-- ReplaceAutoPtr.h ------------ std::auto_ptr replacement -*- 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 of the ReplaceAutoPtrTransform
/// class.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_REPLACE_AUTO_PTR_H
#define CLANG_MODERNIZE_REPLACE_AUTO_PTR_H
#include "Core/Transform.h"
#include "llvm/Support/Compiler.h"
/// \brief Subclass of Transform that transforms the deprecated \c std::auto_ptr
/// into the C++11 \c std::unique_ptr.
///
/// Note that both the \c std::auto_ptr type and the transfer of ownership are
/// transformed. \c std::auto_ptr provides two ways to transfer the ownership,
/// the copy-constructor and the assignment operator. Unlike most classes theses
/// operations do not 'copy' the resource but they 'steal' it.
/// \c std::unique_ptr uses move semantics instead, which makes the intent of
/// transferring the resource explicit. This difference between the two smart
/// pointers requires to wrap the copy-ctor and assign-operator with
/// \c std::move().
///
/// For example, given:
/// \code
/// std::auto_ptr<int> i, j;
/// i = j;
/// \endcode
/// the code is transformed to:
/// \code
/// std::unique_ptr<int> i, j;
/// i = std::move(j);
/// \endcode
class ReplaceAutoPtrTransform : public Transform {
public:
ReplaceAutoPtrTransform(const TransformOptions &Options)
: Transform("ReplaceAutoPtr", Options) {}
/// \see Transform::run().
int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) override;
};
#endif // CLANG_MODERNIZE_REPLACE_AUTO_PTR_H

View File

@ -1,107 +0,0 @@
//===-- ReplaceAutoPtrActions.cpp --- std::auto_ptr replacement -----------===//
//
// 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 contains the definition of the ASTMatcher callback for the
/// ReplaceAutoPtr transform.
///
//===----------------------------------------------------------------------===//
#include "ReplaceAutoPtrActions.h"
#include "Core/Transform.h"
#include "ReplaceAutoPtrMatchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/Lex/Lexer.h"
using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;
namespace {
/// \brief Verifies that the token at \p BeginningOfToken is 'auto_ptr'.
bool checkTokenIsAutoPtr(clang::SourceLocation BeginningOfToken,
const clang::SourceManager &SM,
const clang::LangOptions &LangOptions) {
llvm::SmallVector<char, 8> Buffer;
bool Invalid = false;
llvm::StringRef Res =
Lexer::getSpelling(BeginningOfToken, Buffer, SM, LangOptions, &Invalid);
if (Invalid)
return false;
return Res == "auto_ptr";
}
} // end anonymous namespace
void AutoPtrReplacer::run(const MatchFinder::MatchResult &Result) {
SourceManager &SM = *Result.SourceManager;
SourceLocation IdentifierLoc;
if (const TypeLoc *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
IdentifierLoc = locateFromTypeLoc(*TL, SM);
} else {
const UsingDecl *D = Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId);
assert(D && "Bad Callback. No node provided.");
IdentifierLoc = locateFromUsingDecl(D, SM);
}
if (IdentifierLoc.isMacroID())
IdentifierLoc = SM.getSpellingLoc(IdentifierLoc);
if (!Owner.isFileModifiable(SM, IdentifierLoc))
return;
// make sure that only the 'auto_ptr' token is replaced and not the template
// aliases [temp.alias]
if (!checkTokenIsAutoPtr(IdentifierLoc, SM, LangOptions()))
return;
Owner.addReplacementForCurrentTU(
Replacement(SM, IdentifierLoc, strlen("auto_ptr"), "unique_ptr"));
++AcceptedChanges;
}
SourceLocation AutoPtrReplacer::locateFromTypeLoc(TypeLoc AutoPtrTypeLoc,
const SourceManager &SM) {
TemplateSpecializationTypeLoc TL =
AutoPtrTypeLoc.getAs<TemplateSpecializationTypeLoc>();
if (TL.isNull())
return SourceLocation();
return TL.getTemplateNameLoc();
}
SourceLocation
AutoPtrReplacer::locateFromUsingDecl(const UsingDecl *UsingAutoPtrDecl,
const SourceManager &SM) {
return UsingAutoPtrDecl->getNameInfo().getBeginLoc();
}
void OwnershipTransferFixer::run(const MatchFinder::MatchResult &Result) {
SourceManager &SM = *Result.SourceManager;
const Expr *E = Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId);
assert(E && "Bad Callback. No node provided.");
CharSourceRange Range = Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions());
if (Range.isInvalid())
return;
if (!Owner.isFileModifiable(SM, Range.getBegin()))
return;
Owner.addReplacementForCurrentTU(
Replacement(SM, Range.getBegin(), 0, "std::move("));
Owner.addReplacementForCurrentTU(Replacement(SM, Range.getEnd(), 0, ")"));
AcceptedChanges += 2;
}

View File

@ -1,99 +0,0 @@
//===-- ReplaceAutoPtrActions.h ----- std::auto_ptr replacement -*- 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 contains the declaration of the ASTMatcher callback for the
/// ReplaceAutoPtr transform.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_REPLACE_AUTO_PTR_ACTIONS_H
#define CLANG_MODERNIZE_REPLACE_AUTO_PTR_ACTIONS_H
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Refactoring.h"
class Transform;
/// \brief The callback to be used when replacing the \c std::auto_ptr types and
/// using declarations.
class AutoPtrReplacer : public clang::ast_matchers::MatchFinder::MatchCallback {
public:
AutoPtrReplacer(unsigned &AcceptedChanges, Transform &Owner)
: AcceptedChanges(AcceptedChanges), Owner(Owner) {}
/// \brief Entry point to the callback called when matches are made.
void
run(const clang::ast_matchers::MatchFinder::MatchResult &Result) override;
private:
/// \brief Locates the \c auto_ptr token when it is referred by a \c TypeLoc.
///
/// \code
/// std::auto_ptr<int> i;
/// ^~~~~~~~~~~~~
/// \endcode
/// The caret represents the location returned and the tildes cover the
/// parameter \p AutoPtrTypeLoc.
///
/// \return An invalid \c SourceLocation if not found, otherwise the location
/// of the beginning of the \c auto_ptr token.
clang::SourceLocation locateFromTypeLoc(clang::TypeLoc AutoPtrTypeLoc,
const clang::SourceManager &SM);
/// \brief Locates the \c auto_ptr token in using declarations.
///
/// \code
/// using std::auto_ptr;
/// ^
/// \endcode
/// The caret represents the location returned.
///
/// \return An invalid \c SourceLocation if not found, otherwise the
/// location of the beginning of the \c auto_ptr token.
clang::SourceLocation
locateFromUsingDecl(const clang::UsingDecl *UsingAutoPtrDecl,
const clang::SourceManager &SM);
private:
unsigned &AcceptedChanges;
Transform &Owner;
};
/// \brief The callback to be used to fix the ownership transfers of
/// \c auto_ptr,
///
/// \c unique_ptr requires to use \c std::move() explicitly in order to transfer
/// the ownership.
///
/// Given:
/// \code
/// std::auto_ptr<int> a, b;
/// a = b;
/// \endcode
/// The last statement is transformed to:
/// \code
/// a = std::move(b);
/// \endcode
class OwnershipTransferFixer
: public clang::ast_matchers::MatchFinder::MatchCallback {
public:
OwnershipTransferFixer(unsigned &AcceptedChanges, Transform &Owner)
: AcceptedChanges(AcceptedChanges), Owner(Owner) {}
/// \brief Entry point to the callback called when matches are made.
void
run(const clang::ast_matchers::MatchFinder::MatchResult &Result) override;
private:
unsigned &AcceptedChanges;
Transform &Owner;
};
#endif // CLANG_MODERNIZE_REPLACE_AUTO_PTR_ACTIONS_H

View File

@ -1,81 +0,0 @@
//===-- ReplaceAutoPtrMatchers.cpp -- std::auto_ptr replacement -----------===//
//
// 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 contains the definitions for matcher-generating functions
/// and names for bound nodes found by AST matchers.
///
//===----------------------------------------------------------------------===//
#include "ReplaceAutoPtrMatchers.h"
#include "Core/CustomMatchers.h"
const char *AutoPtrTokenId = "AutoPtrTokenId";
const char *AutoPtrOwnershipTransferId = "AutoPtrOwnershipTransferId";
namespace clang {
namespace ast_matchers {
/// \brief Matches expressions that are lvalues.
///
/// In the following example, a[0] matches expr(isLValue()):
/// \code
/// std::string a[2];
/// std::string b;
/// b = a[0];
/// b = "this string won't match";
/// \endcode
AST_MATCHER(Expr, isLValue) {
return Node.getValueKind() == VK_LValue;
}
} // end namespace ast_matchers
} // end namespace clang
using namespace clang;
using namespace clang::ast_matchers;
// shared matchers
static DeclarationMatcher AutoPtrDecl =
recordDecl(hasName("auto_ptr"), isFromStdNamespace());
static TypeMatcher AutoPtrType = qualType(hasDeclaration(AutoPtrDecl));
// Matcher that finds expressions that are candidates to be wrapped with
// 'std::move()'.
//
// Binds the id \c AutoPtrOwnershipTransferId to the expression.
static StatementMatcher MovableArgumentMatcher = expr(
allOf(isLValue(), hasType(AutoPtrType))).bind(AutoPtrOwnershipTransferId);
TypeLocMatcher makeAutoPtrTypeLocMatcher() {
// skip elaboratedType() as the named type will match soon thereafter.
return typeLoc(loc(qualType(AutoPtrType, unless(elaboratedType()))))
.bind(AutoPtrTokenId);
}
DeclarationMatcher makeAutoPtrUsingDeclMatcher() {
return usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(
allOf(hasName("auto_ptr"), isFromStdNamespace())))).bind(AutoPtrTokenId);
}
StatementMatcher makeTransferOwnershipExprMatcher() {
StatementMatcher assignOperator =
cxxOperatorCallExpr(allOf(
hasOverloadedOperatorName("="),
callee(cxxMethodDecl(ofClass(AutoPtrDecl))),
hasArgument(1, MovableArgumentMatcher)));
StatementMatcher copyCtor =
cxxConstructExpr(allOf(hasType(AutoPtrType),
argumentCountIs(1),
hasArgument(0, MovableArgumentMatcher)));
return anyOf(assignOperator, copyCtor);
}

View File

@ -1,64 +0,0 @@
//===-- ReplaceAutoPtrMatchers.h ---- std::auto_ptr replacement -*- 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 contains the declarations for matcher-generating functions
/// and names for bound nodes found by AST matchers.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H
#define CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H
#include "clang/ASTMatchers/ASTMatchers.h"
/// Names to bind with matched expressions.
extern const char *AutoPtrTokenId;
extern const char *AutoPtrOwnershipTransferId;
/// \brief Creates a matcher that finds the locations of types referring to the
/// \c std::auto_ptr() type.
///
/// \code
/// std::auto_ptr<int> a;
/// ^~~~~~~~~~~~~
///
/// typedef std::auto_ptr<int> int_ptr_t;
/// ^~~~~~~~~~~~~
///
/// std::auto_ptr<int> fn(std::auto_ptr<int>);
/// ^~~~~~~~~~~~~ ^~~~~~~~~~~~~
///
/// <etc...>
/// \endcode
clang::ast_matchers::TypeLocMatcher makeAutoPtrTypeLocMatcher();
/// \brief Creates a matcher that finds the using declarations referring to
/// \c std::auto_ptr.
///
/// \code
/// using std::auto_ptr;
/// ^~~~~~~~~~~~~~~~~~~
/// \endcode
clang::ast_matchers::DeclarationMatcher makeAutoPtrUsingDeclMatcher();
/// \brief Creates a matcher that finds the \c std::auto_ptr copy-ctor and
/// assign-operator expressions.
///
/// \c AutoPtrOwnershipTransferId is assigned to the argument of the expression,
/// this is the part that has to be wrapped by \c std::move().
///
/// \code
/// std::auto_ptr<int> i, j;
/// i = j;
/// ~~~~^
/// \endcode
clang::ast_matchers::StatementMatcher makeTransferOwnershipExprMatcher();
#endif // CLANG_MODERNIZE_REPLACE_AUTO_PTR_MATCHERS_H

View File

@ -1,70 +0,0 @@
//===-- UseAuto/UseAuto.cpp - Use auto type specifier ---------------------===//
//
// 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 UseAutoTransform class.
///
//===----------------------------------------------------------------------===//
#include "UseAuto.h"
#include "UseAutoActions.h"
#include "UseAutoMatchers.h"
using clang::ast_matchers::MatchFinder;
using namespace clang;
using namespace clang::tooling;
int UseAutoTransform::apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool UseAutoTool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
MatchFinder Finder;
ReplacementsVec Replaces;
IteratorReplacer ReplaceIterators(AcceptedChanges, Options().MaxRiskLevel,
/*Owner=*/ *this);
NewReplacer ReplaceNew(AcceptedChanges, Options().MaxRiskLevel,
/*Owner=*/ *this);
Finder.addMatcher(makeIteratorDeclMatcher(), &ReplaceIterators);
Finder.addMatcher(makeDeclWithNewMatcher(), &ReplaceNew);
if (int Result = UseAutoTool.run(createActionFactory(Finder).get())) {
llvm::errs() << "Error encountered during translation.\n";
return Result;
}
setAcceptedChanges(AcceptedChanges);
return 0;
}
namespace {
struct UseAutoFactory : TransformFactory {
UseAutoFactory() {
Since.Clang = Version(2, 9);
Since.Gcc = Version(4, 4);
Since.Icc = Version(12);
Since.Msvc = Version(10);
}
Transform *createTransform(const TransformOptions &Opts) override {
return new UseAutoTransform(Opts);
}
};
} // namespace
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<UseAutoFactory>
X("use-auto", "Use of 'auto' type specifier");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int UseAutoTransformAnchorSource = 0;

View File

@ -1,41 +0,0 @@
//===-- UseAuto/UseAuto.h - Use auto type specifier -------------*- 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 of the UseAutoTransform class
/// which is the main interface to the use-auto transform that replaces
/// type specifiers with the special C++11 'auto' type specifier in certain
/// situations.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_USE_AUTO_H
#define CLANG_MODERNIZE_USE_AUTO_H
#include "Core/Transform.h"
#include "llvm/Support/Compiler.h"
/// \brief Subclass of Transform that transforms type specifiers for variable
/// declarations into the special C++11 'auto' type specifier for certain cases:
/// * Iterators of std containers.
/// * More to come...
///
/// Other uses of the auto type specifier as outlined in C++11 [dcl.spec.auto]
/// p2 are not handled by this transform.
class UseAutoTransform : public Transform {
public:
UseAutoTransform(const TransformOptions &Options)
: Transform("UseAuto", Options) {}
/// \see Transform::run().
int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) override;
};
#endif // CLANG_MODERNIZE_USE_AUTO_H

View File

@ -1,147 +0,0 @@
//===-- UseAuto/UseAutoActions.cpp - Matcher callback 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 contains the implementation of callbacks for the UseAuto
/// transform.
///
//===----------------------------------------------------------------------===//
#include "UseAutoActions.h"
#include "UseAutoMatchers.h"
#include "clang/AST/ASTContext.h"
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace clang;
void IteratorReplacer::run(const MatchFinder::MatchResult &Result) {
const DeclStmt *D = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId);
assert(D && "Bad Callback. No node provided");
SourceManager &SM = *Result.SourceManager;
if (!Owner.isFileModifiable(SM, D->getLocStart()))
return;
for (clang::DeclStmt::const_decl_iterator DI = D->decl_begin(),
DE = D->decl_end();
DI != DE; ++DI) {
const VarDecl *V = cast<VarDecl>(*DI);
const Expr *ExprInit = V->getInit();
// Skip expressions with cleanups from the initializer expression.
if (const ExprWithCleanups *E = dyn_cast<ExprWithCleanups>(ExprInit))
ExprInit = E->getSubExpr();
const CXXConstructExpr *Construct = cast<CXXConstructExpr>(ExprInit);
assert(Construct->getNumArgs() == 1u &&
"Expected constructor with single argument");
// Drill down to the as-written initializer.
const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
if (E != E->IgnoreConversionOperator())
// We hit a conversion operator. Early-out now as they imply an implicit
// conversion from a different type. Could also mean an explicit
// conversion from the same type but that's pretty rare.
return;
if (const CXXConstructExpr *NestedConstruct = dyn_cast<CXXConstructExpr>(E))
// If we ran into an implicit conversion constructor, can't convert.
//
// FIXME: The following only checks if the constructor can be used
// implicitly, not if it actually was. Cases where the converting
// constructor was used explicitly won't get converted.
if (NestedConstruct->getConstructor()->isConvertingConstructor(false))
return;
if (!Result.Context->hasSameType(V->getType(), E->getType()))
return;
}
// Get the type location using the first declartion.
const VarDecl *V = cast<VarDecl>(*D->decl_begin());
TypeLoc TL = V->getTypeSourceInfo()->getTypeLoc();
// WARNING: TypeLoc::getSourceRange() will include the identifier for things
// like function pointers. Not a concern since this action only works with
// iterators but something to keep in mind in the future.
CharSourceRange Range(TL.getSourceRange(), true);
Owner.addReplacementForCurrentTU(tooling::Replacement(SM, Range, "auto"));
++AcceptedChanges;
}
void NewReplacer::run(const MatchFinder::MatchResult &Result) {
const DeclStmt *D = Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId);
assert(D && "Bad Callback. No node provided");
SourceManager &SM = *Result.SourceManager;
if (!Owner.isFileModifiable(SM, D->getLocStart()))
return;
const VarDecl *FirstDecl = cast<VarDecl>(*D->decl_begin());
// Ensure that there is at least one VarDecl within de DeclStmt.
assert(FirstDecl && "No VarDecl provided");
const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
std::vector<SourceLocation> StarLocations;
for (clang::DeclStmt::const_decl_iterator DI = D->decl_begin(),
DE = D->decl_end();
DI != DE; ++DI) {
const VarDecl *V = cast<VarDecl>(*DI);
// Ensure that every DeclStmt child is a VarDecl.
assert(V && "No VarDecl provided");
const CXXNewExpr *NewExpr =
cast<CXXNewExpr>(V->getInit()->IgnoreParenImpCasts());
// Ensure that every VarDecl has a CXXNewExpr initializer.
assert(NewExpr && "No CXXNewExpr provided");
// If VarDecl and Initializer have mismatching unqualified types.
if (!Result.Context->hasSameUnqualifiedType(V->getType(),
NewExpr->getType()))
return;
// Remove explicitly written '*' from declarations where there's more than
// one declaration in the declaration list.
if (DI == D->decl_begin())
continue;
// All subsequent delcarations should match the same non-decorated type.
if (FirstDeclType != V->getType().getCanonicalType())
return;
PointerTypeLoc Q =
V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
while (!Q.isNull()) {
StarLocations.push_back(Q.getStarLoc());
Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
}
}
// Remove '*' from declarations using the saved star locations.
for (std::vector<SourceLocation>::iterator I = StarLocations.begin(),
E = StarLocations.end();
I != E; ++I) {
Owner.addReplacementForCurrentTU(tooling::Replacement(SM, *I, 1, ""));
}
// FIXME: There is, however, one case we can address: when the VarDecl
// pointee is the same as the initializer, just more CV-qualified. However,
// TypeLoc information is not reliable where CV qualifiers are concerned so
// we can't do anything about this case for now.
CharSourceRange Range(
FirstDecl->getTypeSourceInfo()->getTypeLoc().getSourceRange(), true);
// Space after 'auto' to handle cases where the '*' in the pointer type
// is next to the identifier. This avoids changing 'int *p' into 'autop'.
Owner.addReplacementForCurrentTU(tooling::Replacement(SM, Range, "auto "));
++AcceptedChanges;
}

View File

@ -1,56 +0,0 @@
//===-- UseAuto/Actions.h - Matcher callback --------------------*- 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 contains the declarations for callbacks used by the
/// UseAuto transform.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_USE_AUTO_ACTIONS_H
#define CLANG_MODERNIZE_USE_AUTO_ACTIONS_H
#include "Core/Transform.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Refactoring.h"
/// \brief The callback to be used when replacing type specifiers of variable
/// declarations that are iterators.
class IteratorReplacer
: public clang::ast_matchers::MatchFinder::MatchCallback {
public:
IteratorReplacer(unsigned &AcceptedChanges, RiskLevel, Transform &Owner)
: AcceptedChanges(AcceptedChanges), Owner(Owner) {}
/// \brief Entry point to the callback called when matches are made.
void
run(const clang::ast_matchers::MatchFinder::MatchResult &Result) override;
private:
unsigned &AcceptedChanges;
Transform &Owner;
};
/// \brief The callback used when replacing type specifiers of variable
/// declarations initialized by a C++ new expression.
class NewReplacer : public clang::ast_matchers::MatchFinder::MatchCallback {
public:
NewReplacer(unsigned &AcceptedChanges, RiskLevel, Transform &Owner)
: AcceptedChanges(AcceptedChanges), Owner(Owner) {}
/// \brief Entry point to the callback called when matches are made.
void
run(const clang::ast_matchers::MatchFinder::MatchResult &Result) override;
private:
unsigned &AcceptedChanges;
Transform &Owner;
};
#endif // CLANG_MODERNIZE_USE_AUTO_ACTIONS_H

View File

@ -1,280 +0,0 @@
//===-- UseAutoMatchers.cpp - Matchers for use-auto transform -------------===//
//
// 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 contains the implementation for matcher-generating
/// functions and custom AST_MATCHERs.
///
//===----------------------------------------------------------------------===//
#include "UseAutoMatchers.h"
#include "Core/CustomMatchers.h"
#include "clang/AST/ASTContext.h"
using namespace clang::ast_matchers;
using namespace clang;
const char *IteratorDeclStmtId = "iterator_decl";
const char *DeclWithNewId = "decl_new";
const char *NewExprId = "new_expr";
namespace clang {
namespace ast_matchers {
/// \brief Matches variable declarations that have explicit initializers that
/// are not initializer lists.
///
/// Given
/// \code
/// iterator I = Container.begin();
/// MyType A(42);
/// MyType B{2};
/// MyType C;
/// \endcode
/// varDecl(hasWrittenNonListInitializer()) matches \c I and \c A but not \c B
/// or \c C.
AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
const Expr *Init = Node.getAnyInitializer();
if (!Init)
return false;
// The following test is based on DeclPrinter::VisitVarDecl() to find if an
// initializer is implicit or not.
bool ImplicitInit = false;
if (const CXXConstructExpr *Construct = dyn_cast<CXXConstructExpr>(Init)) {
if (Construct->isListInitialization())
return false;
ImplicitInit = Construct->getNumArgs() == 0 ||
Construct->getArg(0)->isDefaultArgument();
} else
if (Node.getInitStyle() == VarDecl::ListInit)
return false;
return !ImplicitInit;
}
/// \brief Matches QualTypes that are type sugar for QualTypes that match \c
/// SugarMatcher.
///
/// Given
/// \code
/// class C {};
/// typedef C my_type
/// typedef my_type my_other_type;
/// \endcode
///
/// \c qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C"))))))
/// matches \c my_type and \c my_other_type.
AST_MATCHER_P(QualType, isSugarFor, internal::Matcher<QualType>, SugarMatcher) {
QualType QT = Node;
for (;;) {
if (SugarMatcher.matches(QT, Finder, Builder))
return true;
QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
if (NewQT == QT)
break;
QT = NewQT;
}
return false;
}
/// \brief Matches named declarations that have one of the standard iterator
/// names: iterator, reverse_iterator, const_iterator, const_reverse_iterator.
///
/// Given
/// \code
/// iterator I;
/// const_iterator CI;
/// \endcode
///
/// \c namedDecl(hasStdIteratorName()) matches \c I and \c CI.
AST_MATCHER(NamedDecl, hasStdIteratorName) {
static const char *const IteratorNames[] = {
"iterator",
"reverse_iterator",
"const_iterator",
"const_reverse_iterator"
};
for (unsigned int i = 0;
i < llvm::array_lengthof(IteratorNames);
++i) {
if (hasName(IteratorNames[i]).matches(Node, Finder, Builder))
return true;
}
return false;
}
/// \brief Matches named declarations that have one of the standard container
/// names.
///
/// Given
/// \code
/// class vector {};
/// class forward_list {};
/// class my_vec {};
/// \endcode
///
/// \c recordDecl(hasStdContainerName()) matches \c vector and \c forward_list
/// but not \c my_vec.
AST_MATCHER(NamedDecl, hasStdContainerName) {
static const char *const ContainerNames[] = {
"array",
"deque",
"forward_list",
"list",
"vector",
"map",
"multimap",
"set",
"multiset",
"unordered_map",
"unordered_multimap",
"unordered_set",
"unordered_multiset",
"queue",
"priority_queue",
"stack"
};
for (unsigned int i = 0; i < llvm::array_lengthof(ContainerNames); ++i) {
if (hasName(ContainerNames[i]).matches(Node, Finder, Builder))
return true;
}
return false;
}
} // namespace ast_matchers
} // namespace clang
namespace {
// \brief Returns a TypeMatcher that matches typedefs for standard iterators
// inside records with a standard container name.
TypeMatcher typedefIterator() {
return typedefType(
hasDeclaration(
allOf(
namedDecl(hasStdIteratorName()),
hasDeclContext(
recordDecl(hasStdContainerName(), isFromStdNamespace())
)
)
)
);
}
// \brief Returns a TypeMatcher that matches records named for standard
// iterators nested inside records named for standard containers.
TypeMatcher nestedIterator() {
return recordType(
hasDeclaration(
allOf(
namedDecl(hasStdIteratorName()),
hasDeclContext(
recordDecl(hasStdContainerName(), isFromStdNamespace())
)
)
)
);
}
// \brief Returns a TypeMatcher that matches types declared with using
// declarations and which name standard iterators for standard containers.
TypeMatcher iteratorFromUsingDeclaration() {
// Types resulting from using declarations are
// represented by ElaboratedType.
return elaboratedType(
allOf(
// Unwrap the nested name specifier to test for
// one of the standard containers.
hasQualifier(
specifiesType(
templateSpecializationType(
hasDeclaration(
namedDecl(hasStdContainerName(), isFromStdNamespace())
)
)
)
),
// The named type is what comes after the final
// '::' in the type. It should name one of the
// standard iterator names.
namesType(anyOf(
typedefType(
hasDeclaration(
namedDecl(hasStdIteratorName())
)
),
recordType(
hasDeclaration(
namedDecl(hasStdIteratorName())
)
)
))
)
);
}
} // namespace
// \brief This matcher returns delaration statements that contain variable
// declarations with written non-list initializer for standard iterators.
StatementMatcher makeIteratorDeclMatcher() {
return declStmt(
// At least one varDecl should be a child of the declStmt to ensure it's a
// declaration list and avoid matching other declarations
// e.g. using directives.
has(varDecl()),
unless(has(varDecl(
anyOf(
unless(hasWrittenNonListInitializer()),
hasType(autoType()),
unless(hasType(
isSugarFor(
anyOf(
typedefIterator(),
nestedIterator(),
iteratorFromUsingDeclaration()
)
)
))
)
)))
).bind(IteratorDeclStmtId);
}
StatementMatcher makeDeclWithNewMatcher() {
return declStmt(
has(varDecl()),
unless(has(varDecl(
anyOf(
unless(hasInitializer(
ignoringParenImpCasts(cxxNewExpr())
)),
// FIXME: TypeLoc information is not reliable where CV qualifiers are
// concerned so these types can't be handled for now.
hasType(pointerType(pointee(hasCanonicalType(hasLocalQualifiers())))),
// FIXME: Handle function pointers. For now we ignore them because
// the replacement replaces the entire type specifier source range
// which includes the identifier.
hasType(
pointsTo(
pointsTo(
parenType(innerType(functionType()))
)
)
)
)
)))
).bind(DeclWithNewId);
}

View File

@ -1,34 +0,0 @@
//===-- UseAutoMatchers.h - Matchers for use-auto transform -----*- 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 contains the declarations for matcher-generating functions
/// and names for bound nodes found by AST matchers.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_USE_AUTO_MATCHERS_H
#define CLANG_MODERNIZE_USE_AUTO_MATCHERS_H
#include "clang/ASTMatchers/ASTMatchers.h"
extern const char *IteratorDeclStmtId;
extern const char *DeclWithNewId;
extern const char *NewExprId;
/// \brief Create a matcher that matches declaration staments that have
/// variable declarations where the type is an iterator for an std container
/// and has an explicit initializer of the same type.
clang::ast_matchers::StatementMatcher makeIteratorDeclMatcher();
/// \brief Create a matcher that matches variable declarations that are
/// initialized by a C++ new expression.
clang::ast_matchers::StatementMatcher makeDeclWithNewMatcher();
#endif // CLANG_MODERNIZE_USE_AUTO_MATCHERS_H

View File

@ -1,441 +0,0 @@
//===-- UseNullptr/NullptrActions.cpp - Matcher callback ------------------===//
//
// 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 contains the definition of the NullptrFixer class which is
/// used as an ASTMatcher callback. Also within this file is a helper AST
/// visitor class used to identify sequences of explicit casts.
///
//===----------------------------------------------------------------------===//
#include "NullptrActions.h"
#include "NullptrMatchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace clang;
namespace cl = llvm::cl;
namespace {
const char *NullMacroName = "NULL";
bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
const SourceManager &SM, const Transform &Owner) {
return SM.isWrittenInSameFile(StartLoc, EndLoc) &&
Owner.isFileModifiable(SM, StartLoc);
}
/// \brief Replaces the provided range with the text "nullptr", but only if
/// the start and end location are both in main file.
/// Returns true if and only if a replacement was made.
void ReplaceWithNullptr(Transform &Owner, SourceManager &SM,
SourceLocation StartLoc, SourceLocation EndLoc) {
CharSourceRange Range(SourceRange(StartLoc, EndLoc), true);
// Add a space if nullptr follows an alphanumeric character. This happens
// whenever there is an c-style explicit cast to nullptr not surrounded by
// parentheses and right beside a return statement.
SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
if (isAlphanumeric(*FullSourceLoc(PreviousLocation, SM).getCharacterData()))
Owner.addReplacementForCurrentTU(
tooling::Replacement(SM, Range, " nullptr"));
else
Owner.addReplacementForCurrentTU(
tooling::Replacement(SM, Range, "nullptr"));
}
/// \brief Returns the name of the outermost macro.
///
/// Given
/// \code
/// #define MY_NULL NULL
/// \endcode
/// If \p Loc points to NULL, this function will return the name MY_NULL.
llvm::StringRef GetOutermostMacroName(
SourceLocation Loc, const SourceManager &SM, const LangOptions &LO) {
assert(Loc.isMacroID());
SourceLocation OutermostMacroLoc;
while (Loc.isMacroID()) {
OutermostMacroLoc = Loc;
Loc = SM.getImmediateMacroCallerLoc(Loc);
}
return clang::Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
}
/// \brief RecursiveASTVisitor for ensuring all nodes rooted at a given AST
/// subtree that have file-level source locations corresponding to a macro
/// argument have implicit NullTo(Member)Pointer nodes as ancestors.
class MacroArgUsageVisitor : public RecursiveASTVisitor<MacroArgUsageVisitor> {
public:
MacroArgUsageVisitor(SourceLocation CastLoc, const SourceManager &SM)
: CastLoc(CastLoc), SM(SM), Visited(false), CastFound(false),
InvalidFound(false) {
assert(CastLoc.isFileID());
}
bool TraverseStmt(Stmt *S) {
bool VisitedPreviously = Visited;
if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
return false;
// The point at which VisitedPreviously is false and Visited is true is the
// root of a subtree containing nodes whose locations match CastLoc. It's
// at this point we test that the Implicit NullTo(Member)Pointer cast was
// found or not.
if (!VisitedPreviously) {
if (Visited && !CastFound) {
// Found nodes with matching SourceLocations but didn't come across a
// cast. This is an invalid macro arg use. Can stop traversal
// completely now.
InvalidFound = true;
return false;
}
// Reset state as we unwind back up the tree.
CastFound = false;
Visited = false;
}
return true;
}
bool VisitStmt(Stmt *S) {
if (SM.getFileLoc(S->getLocStart()) != CastLoc)
return true;
Visited = true;
const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
Cast->getCastKind() == CK_NullToMemberPointer))
CastFound = true;
return true;
}
bool foundInvalid() const { return InvalidFound; }
private:
SourceLocation CastLoc;
const SourceManager &SM;
bool Visited;
bool CastFound;
bool InvalidFound;
};
/// \brief Looks for implicit casts as well as sequences of 0 or more explicit
/// casts with an implicit null-to-pointer cast within.
///
/// The matcher this visitor is used with will find a single implicit cast or a
/// top-most explicit cast (i.e. it has no explicit casts as an ancestor) where
/// an implicit cast is nested within. However, there is no guarantee that only
/// explicit casts exist between the found top-most explicit cast and the
/// possibly more than one nested implicit cast. This visitor finds all cast
/// sequences with an implicit cast to null within and creates a replacement
/// leaving the outermost explicit cast unchanged to avoid introducing
/// ambiguities.
class CastSequenceVisitor : public RecursiveASTVisitor<CastSequenceVisitor> {
public:
CastSequenceVisitor(ASTContext &Context, const UserMacroNames &UserNullMacros,
unsigned &AcceptedChanges, Transform &Owner)
: SM(Context.getSourceManager()), Context(Context),
UserNullMacros(UserNullMacros), AcceptedChanges(AcceptedChanges),
Owner(Owner), FirstSubExpr(nullptr), PruneSubtree(false) {}
bool TraverseStmt(Stmt *S) {
// Stop traversing down the tree if requested.
if (PruneSubtree) {
PruneSubtree = false;
return true;
}
return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
}
// Only VisitStmt is overridden as we shouldn't find other base AST types
// within a cast expression.
bool VisitStmt(Stmt *S) {
CastExpr *C = dyn_cast<CastExpr>(S);
if (!C) {
FirstSubExpr = nullptr;
return true;
} else if (!FirstSubExpr) {
FirstSubExpr = C->getSubExpr()->IgnoreParens();
}
if (C->getCastKind() == CK_NullToPointer ||
C->getCastKind() == CK_NullToMemberPointer) {
SourceLocation StartLoc = FirstSubExpr->getLocStart();
SourceLocation EndLoc = FirstSubExpr->getLocEnd();
// If the location comes from a macro arg expansion, *all* uses of that
// arg must be checked to result in NullTo(Member)Pointer casts.
//
// If the location comes from a macro body expansion, check to see if its
// coming from one of the allowed 'NULL' macros.
if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
FileLocEnd = SM.getFileLoc(EndLoc);
if (isReplaceableRange(FileLocStart, FileLocEnd, SM, Owner) &&
allArgUsesValid(C)) {
ReplaceWithNullptr(Owner, SM, FileLocStart, FileLocEnd);
++AcceptedChanges;
}
return skipSubTree();
}
if (SM.isMacroBodyExpansion(StartLoc) &&
SM.isMacroBodyExpansion(EndLoc)) {
llvm::StringRef OutermostMacroName =
GetOutermostMacroName(StartLoc, SM, Context.getLangOpts());
// Check to see if the user wants to replace the macro being expanded.
if (std::find(UserNullMacros.begin(), UserNullMacros.end(),
OutermostMacroName) == UserNullMacros.end()) {
return skipSubTree();
}
StartLoc = SM.getFileLoc(StartLoc);
EndLoc = SM.getFileLoc(EndLoc);
}
if (!isReplaceableRange(StartLoc, EndLoc, SM, Owner)) {
return skipSubTree();
}
ReplaceWithNullptr(Owner, SM, StartLoc, EndLoc);
++AcceptedChanges;
return skipSubTree();
} // If NullTo(Member)Pointer cast.
return true;
}
private:
bool skipSubTree() { PruneSubtree = true; return true; }
/// \brief Tests that all expansions of a macro arg, one of which expands to
/// result in \p CE, yield NullTo(Member)Pointer casts.
bool allArgUsesValid(const CastExpr *CE) {
SourceLocation CastLoc = CE->getLocStart();
// Step 1: Get location of macro arg and location of the macro the arg was
// provided to.
SourceLocation ArgLoc, MacroLoc;
if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
return false;
// Step 2: Find the first ancestor that doesn't expand from this macro.
ast_type_traits::DynTypedNode ContainingAncestor;
if (!findContainingAncestor(
ast_type_traits::DynTypedNode::create<Stmt>(*CE), MacroLoc,
ContainingAncestor))
return false;
// Step 3:
// Visit children of this containing parent looking for the least-descended
// nodes of the containing parent which are macro arg expansions that expand
// from the given arg location.
// Visitor needs: arg loc
MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
if (const Decl *D = ContainingAncestor.get<Decl>())
ArgUsageVisitor.TraverseDecl(const_cast<Decl *>(D));
else if (const Stmt *S = ContainingAncestor.get<Stmt>())
ArgUsageVisitor.TraverseStmt(const_cast<Stmt *>(S));
else
llvm_unreachable("Unhandled ContainingAncestor node type");
if (ArgUsageVisitor.foundInvalid())
return false;
return true;
}
/// \brief Given the SourceLocation for a macro arg expansion, finds the
/// non-macro SourceLocation of the macro the arg was passed to and the
/// non-macro SourceLocation of the argument in the arg list to that macro.
/// These results are returned via \c MacroLoc and \c ArgLoc respectively.
/// These values are undefined if the return value is false.
///
/// \returns false if one of the returned SourceLocations would be a
/// SourceLocation pointing within the definition of another macro.
bool getMacroAndArgLocations(SourceLocation Loc, SourceLocation &ArgLoc,
SourceLocation &MacroLoc) {
assert(Loc.isMacroID() && "Only reasonble to call this on macros");
ArgLoc = Loc;
// Find the location of the immediate macro expansion.
while (1) {
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
SourceLocation OldArgLoc = ArgLoc;
ArgLoc = Expansion.getExpansionLocStart();
if (!Expansion.isMacroArgExpansion()) {
if (!MacroLoc.isFileID())
return false;
StringRef Name =
Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
return std::find(UserNullMacros.begin(), UserNullMacros.end(), Name) !=
UserNullMacros.end();
}
MacroLoc = SM.getImmediateExpansionRange(ArgLoc).first;
ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
if (ArgLoc.isFileID())
return true;
// If spelling location resides in the same FileID as macro expansion
// location, it means there is no inner macro.
FileID MacroFID = SM.getFileID(MacroLoc);
if (SM.isInFileID(ArgLoc, MacroFID))
// Don't transform this case. If the characters that caused the
// null-conversion come from within a macro, they can't be changed.
return false;
}
llvm_unreachable("getMacroAndArgLocations");
}
/// \brief Tests if TestMacroLoc is found while recursively unravelling
/// expansions starting at TestLoc. TestMacroLoc.isFileID() must be true.
/// Implementation is very similar to getMacroAndArgLocations() except in this
/// case, it's not assumed that TestLoc is expanded from a macro argument.
/// While unravelling expansions macro arguments are handled as with
/// getMacroAndArgLocations() but in this function macro body expansions are
/// also handled.
///
/// False means either:
/// - TestLoc is not from a macro expansion
/// - TestLoc is from a different macro expansion
bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
if (TestLoc.isFileID()) {
return false;
}
SourceLocation Loc = TestLoc, MacroLoc;
while (1) {
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
Loc = Expansion.getExpansionLocStart();
if (!Expansion.isMacroArgExpansion()) {
if (Loc.isFileID()) {
if (Loc == TestMacroLoc)
// Match made.
return true;
return false;
}
// Since Loc is still a macro ID and it's not an argument expansion, we
// don't need to do the work of handling an argument expansion. Simply
// keep recursively expanding until we hit a FileID or a macro arg
// expansion or a macro arg expansion.
continue;
}
MacroLoc = SM.getImmediateExpansionRange(Loc).first;
if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc)
// Match made.
return true;
Loc = Expansion.getSpellingLoc();
Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
if (Loc.isFileID())
// If we made it this far without finding a match, there is no match to
// be made.
return false;
}
llvm_unreachable("expandsFrom");
}
/// \brief Given a starting point \c Start in the AST, find an ancestor that
/// doesn't expand from the macro called at file location \c MacroLoc.
///
/// \pre MacroLoc.isFileID()
/// \returns true if such an ancestor was found, false otherwise.
bool findContainingAncestor(ast_type_traits::DynTypedNode Start,
SourceLocation MacroLoc,
ast_type_traits::DynTypedNode &Result) {
// Below we're only following the first parent back up the AST. This should
// be fine since for the statements we care about there should only be one
// parent as far up as we care. If this assumption doesn't hold, need to
// revisit what to do here.
assert(MacroLoc.isFileID());
do {
const auto &Parents = Context.getParents(Start);
if (Parents.empty())
return false;
assert(Parents.size() == 1 &&
"Found an ancestor with more than one parent!");
const ast_type_traits::DynTypedNode &Parent = Parents[0];
SourceLocation Loc;
if (const Decl *D = Parent.get<Decl>())
Loc = D->getLocStart();
else if (const Stmt *S = Parent.get<Stmt>())
Loc = S->getLocStart();
else
llvm_unreachable("Expected to find Decl or Stmt containing ancestor");
if (!expandsFrom(Loc, MacroLoc)) {
Result = Parent;
return true;
}
Start = Parent;
} while (1);
llvm_unreachable("findContainingAncestor");
}
private:
SourceManager &SM;
ASTContext &Context;
const UserMacroNames &UserNullMacros;
unsigned &AcceptedChanges;
Transform &Owner;
Expr *FirstSubExpr;
bool PruneSubtree;
};
} // namespace
NullptrFixer::NullptrFixer(unsigned &AcceptedChanges,
llvm::ArrayRef<llvm::StringRef> UserMacros,
Transform &Owner)
: AcceptedChanges(AcceptedChanges), Owner(Owner) {
UserNullMacros.insert(UserNullMacros.begin(), UserMacros.begin(),
UserMacros.end());
UserNullMacros.insert(UserNullMacros.begin(), llvm::StringRef(NullMacroName));
}
void NullptrFixer::run(const ast_matchers::MatchFinder::MatchResult &Result) {
const CastExpr *NullCast = Result.Nodes.getNodeAs<CastExpr>(CastSequence);
assert(NullCast && "Bad Callback. No node provided");
// Given an implicit null-ptr cast or an explicit cast with an implicit
// null-to-pointer cast within use CastSequenceVisitor to identify sequences
// of explicit casts that can be converted into 'nullptr'.
CastSequenceVisitor Visitor(*Result.Context, UserNullMacros, AcceptedChanges,
Owner);
Visitor.TraverseStmt(const_cast<CastExpr *>(NullCast));
}

View File

@ -1,43 +0,0 @@
//===-- UseNullptr/NullptrActions.h - Matcher callback ----------*- 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 contains the declaration of the NullptrFixer class which
/// is used as a ASTMatcher callback.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_NULLPTR_ACTIONS_H
#define CLANG_MODERNIZE_NULLPTR_ACTIONS_H
#include "Core/Transform.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Refactoring.h"
// The type for user-defined macro names that behave like NULL
typedef llvm::SmallVector<llvm::StringRef, 1> UserMacroNames;
/// \brief The callback to be used for nullptr migration matchers.
///
class NullptrFixer : public clang::ast_matchers::MatchFinder::MatchCallback {
public:
NullptrFixer(unsigned &AcceptedChanges,
llvm::ArrayRef<llvm::StringRef> UserMacros, Transform &Owner);
/// \brief Entry point to the callback called when matches are made.
void
run(const clang::ast_matchers::MatchFinder::MatchResult &Result) override;
private:
unsigned &AcceptedChanges;
UserMacroNames UserNullMacros;
Transform &Owner;
};
#endif // CLANG_MODERNIZE_NULLPTR_ACTIONS_H

View File

@ -1,70 +0,0 @@
//===-- UseNullptr/NullptrMatchers.cpp - Matchers for null casts ----------===//
//
// 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 contains the definitions for matcher-generating functions
/// and a custom AST_MATCHER for identifying casts of type CK_NullTo*.
///
//===----------------------------------------------------------------------===//
#include "NullptrMatchers.h"
#include "clang/AST/ASTContext.h"
using namespace clang::ast_matchers;
using namespace clang;
const char *CastSequence = "sequence";
namespace clang {
namespace ast_matchers {
/// \brief Matches cast expressions that have a cast kind of CK_NullToPointer
/// or CK_NullToMemberPointer.
///
/// Given
/// \code
/// int *p = 0;
/// \endcode
/// implicitCastExpr(isNullToPointer()) matches the implicit cast clang adds
/// around \c 0.
AST_MATCHER(CastExpr, isNullToPointer) {
return Node.getCastKind() == CK_NullToPointer ||
Node.getCastKind() == CK_NullToMemberPointer;
}
AST_MATCHER(Type, sugaredNullptrType) {
const Type *DesugaredType = Node.getUnqualifiedDesugaredType();
if (const BuiltinType *BT = dyn_cast<BuiltinType>(DesugaredType))
return BT->getKind() == BuiltinType::NullPtr;
return false;
}
} // end namespace ast_matchers
} // end namespace clang
StatementMatcher makeCastSequenceMatcher() {
StatementMatcher ImplicitCastToNull =
implicitCastExpr(
isNullToPointer(),
unless(
hasSourceExpression(
hasType(sugaredNullptrType())
)
)
);
return castExpr(
anyOf(
ImplicitCastToNull,
explicitCastExpr(
hasDescendant(ImplicitCastToNull)
)
),
unless(hasAncestor(explicitCastExpr()))
).bind(CastSequence);
}

View File

@ -1,31 +0,0 @@
//===-- UseNullptr/NullptrMatchers.h - Matchers for null casts --*- 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 contains the declarations for matcher-generating functions
/// and names for bound nodes found by AST matchers.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_USE_NULLPTR_MATCHERS_H
#define CLANG_MODERNIZE_USE_NULLPTR_MATCHERS_H
#include "clang/ASTMatchers/ASTMatchers.h"
// Names to bind with matched expressions.
extern const char *CastSequence;
/// \brief Create a matcher that finds implicit casts as well as the head of a
/// sequence of zero or more nested explicit casts that have an implicit cast
/// to null within.
/// Finding sequences of explict casts is necessary so that an entire sequence
/// can be replaced instead of just the inner-most implicit cast.
clang::ast_matchers::StatementMatcher makeCastSequenceMatcher();
#endif // CLANG_MODERNIZE_USE_NULLPTR_MATCHERS_H

View File

@ -1,80 +0,0 @@
//===-- UseNullptr/UseNullptr.cpp - C++11 nullptr migration ---------------===//
//
// 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 UseNullptrTransform
/// class.
///
//===----------------------------------------------------------------------===//
#include "UseNullptr.h"
#include "NullptrActions.h"
#include "NullptrMatchers.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
using clang::ast_matchers::MatchFinder;
using namespace clang::tooling;
using namespace clang;
namespace cl = llvm::cl;
static cl::opt<std::string>
UserNullMacroNames("user-null-macros",
cl::desc("Comma-separated list of user-defined "
"macro names that behave like NULL"),
cl::cat(TransformsOptionsCategory), cl::init(""));
int UseNullptrTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool UseNullptrTool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
llvm::SmallVector<llvm::StringRef, 1> MacroNames;
if (!UserNullMacroNames.empty()) {
llvm::StringRef S = UserNullMacroNames;
S.split(MacroNames, ",");
}
MatchFinder Finder;
NullptrFixer Fixer(AcceptedChanges, MacroNames, /*Owner=*/ *this);
Finder.addMatcher(makeCastSequenceMatcher(), &Fixer);
if (int result = UseNullptrTool.run(createActionFactory(Finder).get())) {
llvm::errs() << "Error encountered during translation.\n";
return result;
}
setAcceptedChanges(AcceptedChanges);
return 0;
}
namespace {
struct UseNullptrFactory : TransformFactory {
UseNullptrFactory() {
Since.Clang = Version(3, 0);
Since.Gcc = Version(4, 6);
Since.Icc = Version(12, 1);
Since.Msvc = Version(10);
}
Transform *createTransform(const TransformOptions &Opts) override {
return new UseNullptrTransform(Opts);
}
};
} // namespace
// Register the factory using this statically initialized variable.
static TransformFactoryRegistry::Add<UseNullptrFactory>
X("use-nullptr", "Make use of nullptr keyword where possible");
// This anchor is used to force the linker to link in the generated object file
// and thus register the factory.
volatile int UseNullptrTransformAnchorSource = 0;

View File

@ -1,35 +0,0 @@
//===-- UseNullptr/UseNullptr.h - C++11 nullptr migration -------*- 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 of the UseNullptrTransform
/// class which is the main interface to the use-nullptr transform that tries to
/// make use of nullptr where possible.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_USE_NULLPTR_H
#define CLANG_MODERNIZE_USE_NULLPTR_H
#include "Core/Transform.h"
#include "llvm/Support/Compiler.h" // For override
/// \brief Subclass of Transform that transforms null pointer constants into
/// C++11's nullptr keyword where possible.
class UseNullptrTransform : public Transform {
public:
UseNullptrTransform(const TransformOptions &Options)
: Transform("UseNullptr", Options) {}
/// \see Transform::run().
int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) override;
};
#endif // CLANG_MODERNIZE_USE_NULLPTR_H

View File

@ -1,49 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
set (ClangModernizeSources
ClangModernize.cpp
)
# FIXME: Lib-ify the transforms to simplify the build rules.
# For each transform subdirectory.
file(GLOB_RECURSE LoopConvertSources "../LoopConvert/*.cpp")
list(APPEND ClangModernizeSources ${LoopConvertSources})
file(GLOB_RECURSE UseNullptrSources "../UseNullptr/*.cpp")
list(APPEND ClangModernizeSources ${UseNullptrSources})
file(GLOB_RECURSE UseAutoSources "../UseAuto/*.cpp")
list(APPEND ClangModernizeSources ${UseAutoSources})
file(GLOB_RECURSE AddOverrideSources "../AddOverride/*.cpp")
list(APPEND ClangModernizeSources ${AddOverrideSources})
file(GLOB_RECURSE PassByValueSources "../PassByValue/*.cpp")
list(APPEND ClangModernizeSources ${PassByValueSources})
file(GLOB_RECURSE ReplaceAutoPtrSources "../ReplaceAutoPtr/*.cpp")
list(APPEND ClangModernizeSources ${ReplaceAutoPtrSources})
add_clang_executable(clang-modernize
${ClangModernizeSources}
)
add_dependencies(clang-modernize
clang-headers clang-apply-replacements
)
target_link_libraries(clang-modernize
clangAST
clangASTMatchers
clangBasic
clangFormat
clangFrontend
clangLex
clangTooling
clangToolingCore
modernizeCore
)
install(TARGETS clang-modernize
RUNTIME DESTINATION bin)

View File

@ -1,489 +0,0 @@
//===-- ClangModernize.cpp - Main file for Clang modernization tool -------===//
//
// 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 implements the C++11 feature migration tool main function
/// and transformation framework.
///
/// See user documentation for usage instructions.
///
//===----------------------------------------------------------------------===//
#include "Core/PerfSupport.h"
#include "Core/ReplacementHandling.h"
#include "Core/Transform.h"
#include "Core/Transforms.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Format/Format.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
namespace cl = llvm::cl;
using namespace clang;
using namespace clang::tooling;
static TransformOptions GlobalOptions;
// All options must belong to locally defined categories for them to get shown
// by -help. We explicitly hide everything else (except -help and -version).
static cl::OptionCategory GeneralCategory("Modernizer Options");
static cl::OptionCategory FormattingCategory("Formatting Options");
static cl::OptionCategory IncludeExcludeCategory("Inclusion/Exclusion Options");
static cl::OptionCategory SerializeCategory("Serialization Options");
static const cl::OptionCategory *const VisibleCategories[] = {
&GeneralCategory, &FormattingCategory, &IncludeExcludeCategory,
&SerializeCategory, &TransformCategory, &TransformsOptionsCategory,
};
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static cl::extrahelp MoreHelp(
"EXAMPLES:\n\n"
"Apply all transforms on a file that doesn't require compilation arguments:\n\n"
" clang-modernize file.cpp\n"
"\n"
"Convert for loops to ranged-based for loops for all files in the compilation\n"
"database that belong in a project subtree and then reformat the code\n"
"automatically using the LLVM style:\n\n"
" clang-modernize -p build/path -include project/path -format -loop-convert\n"
"\n"
"Make use of both nullptr and the override specifier, using git ls-files:\n"
"\n"
" git ls-files '*.cpp' | xargs -I{} clang-modernize -p build/path \\\n"
" -use-nullptr -add-override -override-macros {}\n"
"\n"
"Apply all transforms supported by both clang >= 3.0 and gcc >= 4.7 to\n"
"foo.cpp and any included headers in bar:\n\n"
" clang-modernize -for-compilers=clang-3.0,gcc-4.7 foo.cpp \\\n"
" -include bar -- -std=c++11 -Ibar\n\n");
////////////////////////////////////////////////////////////////////////////////
/// General Options
// This is set to hidden on purpose. The actual help text for this option is
// included in CommonOptionsParser::HelpMessage.
static cl::opt<std::string> BuildPath("p", cl::desc("Build Path"), cl::Optional,
cl::Hidden, cl::cat(GeneralCategory));
static cl::list<std::string> SourcePaths(cl::Positional,
cl::desc("[<sources>...]"),
cl::ZeroOrMore,
cl::cat(GeneralCategory));
static cl::opt<RiskLevel, /*ExternalStorage=*/true> MaxRiskLevel(
"risk", cl::desc("Select a maximum risk level:"),
cl::values(clEnumValN(RL_Safe, "safe", "Only safe transformations"),
clEnumValN(RL_Reasonable, "reasonable",
"Enable transformations that might change "
"semantics (default)"),
clEnumValN(RL_Risky, "risky",
"Enable transformations that are likely to "
"change semantics"),
clEnumValEnd),
cl::location(GlobalOptions.MaxRiskLevel), cl::init(RL_Reasonable),
cl::cat(GeneralCategory));
static cl::opt<bool> FinalSyntaxCheck(
"final-syntax-check",
cl::desc("Check for correct syntax after applying transformations"),
cl::init(false), cl::cat(GeneralCategory));
static cl::opt<bool> SummaryMode("summary", cl::desc("Print transform summary"),
cl::init(false), cl::cat(GeneralCategory));
static cl::opt<std::string>
TimingDirectoryName("perf",
cl::desc("Capture performance data and output to specified "
"directory. Default: ./migrate_perf"),
cl::ValueOptional, cl::value_desc("directory name"),
cl::cat(GeneralCategory));
static cl::opt<std::string> SupportedCompilers(
"for-compilers", cl::value_desc("string"),
cl::desc("Select transforms targeting the intersection of\n"
"language features supported by the given compilers.\n"
"Takes a comma-separated list of <compiler>-<version>.\n"
"\t<compiler> can be any of: clang, gcc, icc, msvc\n"
"\t<version> is <major>[.<minor>]\n"),
cl::cat(GeneralCategory));
////////////////////////////////////////////////////////////////////////////////
/// Format Options
static cl::opt<bool> DoFormat(
"format",
cl::desc("Enable formatting of code changed by applying replacements.\n"
"Use -style to choose formatting style.\n"),
cl::cat(FormattingCategory));
static cl::opt<std::string>
FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
cl::init("LLVM"), cl::cat(FormattingCategory));
// FIXME: Consider making the default behaviour for finding a style
// configuration file to start the search anew for every file being changed to
// handle situations where the style is different for different parts of a
// project.
static cl::opt<std::string> FormatStyleConfig(
"style-config",
cl::desc("Path to a directory containing a .clang-format file\n"
"describing a formatting style to use for formatting\n"
"code when -style=file.\n"),
cl::init(""), cl::cat(FormattingCategory));
////////////////////////////////////////////////////////////////////////////////
/// Include/Exclude Options
static cl::opt<std::string>
IncludePaths("include",
cl::desc("Comma-separated list of paths to consider to be "
"transformed"),
cl::cat(IncludeExcludeCategory));
static cl::opt<std::string>
ExcludePaths("exclude", cl::desc("Comma-separated list of paths that can not "
"be transformed"),
cl::cat(IncludeExcludeCategory));
static cl::opt<std::string>
IncludeFromFile("include-from", cl::value_desc("filename"),
cl::desc("File containing a list of paths to consider to "
"be transformed"),
cl::cat(IncludeExcludeCategory));
static cl::opt<std::string>
ExcludeFromFile("exclude-from", cl::value_desc("filename"),
cl::desc("File containing a list of paths that can not be "
"transformed"),
cl::cat(IncludeExcludeCategory));
////////////////////////////////////////////////////////////////////////////////
/// Serialization Options
static cl::opt<bool>
SerializeOnly("serialize-replacements",
cl::desc("Serialize translation unit replacements to "
"disk instead of changing files."),
cl::init(false),
cl::cat(SerializeCategory));
static cl::opt<std::string>
SerializeLocation("serialize-dir",
cl::desc("Path to an existing directory in which to write\n"
"serialized replacements. Default behaviour is to\n"
"write to a temporary directory.\n"),
cl::cat(SerializeCategory));
////////////////////////////////////////////////////////////////////////////////
static void printVersion() {
llvm::outs() << "clang-modernizer version " CLANG_VERSION_STRING
<< "\n";
}
/// \brief Extract the minimum compiler versions as requested on the command
/// line by the switch \c -for-compilers.
///
/// \param ProgName The name of the program, \c argv[0], used to print errors.
/// \param Error If an error occur while parsing the versions this parameter is
/// set to \c true, otherwise it will be left untouched.
static CompilerVersions handleSupportedCompilers(const char *ProgName,
bool &Error) {
if (SupportedCompilers.getNumOccurrences() == 0)
return CompilerVersions();
CompilerVersions RequiredVersions;
llvm::SmallVector<llvm::StringRef, 4> Compilers;
llvm::StringRef(SupportedCompilers).split(Compilers, ",");
for (llvm::SmallVectorImpl<llvm::StringRef>::iterator I = Compilers.begin(),
E = Compilers.end();
I != E; ++I) {
llvm::StringRef Compiler, VersionStr;
std::tie(Compiler, VersionStr) = I->split('-');
Version *V = llvm::StringSwitch<Version *>(Compiler)
.Case("clang", &RequiredVersions.Clang)
.Case("gcc", &RequiredVersions.Gcc).Case("icc", &RequiredVersions.Icc)
.Case("msvc", &RequiredVersions.Msvc).Default(nullptr);
if (V == nullptr) {
llvm::errs() << ProgName << ": " << Compiler
<< ": unsupported platform\n";
Error = true;
continue;
}
if (VersionStr.empty()) {
llvm::errs() << ProgName << ": " << *I
<< ": missing version number in platform\n";
Error = true;
continue;
}
Version Version = Version::getFromString(VersionStr);
if (Version.isNull()) {
llvm::errs()
<< ProgName << ": " << *I
<< ": invalid version, please use \"<major>[.<minor>]\" instead of \""
<< VersionStr << "\"\n";
Error = true;
continue;
}
// support the lowest version given
if (V->isNull() || Version < *V)
*V = Version;
}
return RequiredVersions;
}
static std::unique_ptr<CompilationDatabase>
autoDetectCompilations(std::string &ErrorMessage) {
// Auto-detect a compilation database from BuildPath.
if (BuildPath.getNumOccurrences() > 0)
return CompilationDatabase::autoDetectFromDirectory(BuildPath,
ErrorMessage);
// Try to auto-detect a compilation database from the first source.
if (!SourcePaths.empty()) {
if (std::unique_ptr<CompilationDatabase> Compilations =
CompilationDatabase::autoDetectFromSource(SourcePaths[0],
ErrorMessage)) {
// FIXME: just pass SourcePaths[0] once getCompileCommands supports
// non-absolute paths.
SmallString<64> Path(SourcePaths[0]);
llvm::sys::fs::make_absolute(Path);
std::vector<CompileCommand> Commands =
Compilations->getCompileCommands(Path);
// Ignore a detected compilation database that doesn't contain source0
// since it is probably an unrelated compilation database.
if (!Commands.empty())
return Compilations;
}
// Reset ErrorMessage since a fix compilation database will be created if
// it fails to detect one from source.
ErrorMessage = "";
// If no compilation database can be detected from source then we create a
// fixed compilation database with c++11 support.
std::string CommandLine[] = { "-std=c++11" };
return llvm::make_unique<FixedCompilationDatabase>(".", CommandLine);
}
ErrorMessage = "Could not determine sources to transform";
return nullptr;
}
// Predicate definition for determining whether a file is not included.
static bool isFileNotIncludedPredicate(llvm::StringRef FilePath) {
return !GlobalOptions.ModifiableFiles.isFileIncluded(FilePath);
}
// Predicate definition for determining if a file was explicitly excluded.
static bool isFileExplicitlyExcludedPredicate(llvm::StringRef FilePath) {
if (GlobalOptions.ModifiableFiles.isFileExplicitlyExcluded(FilePath)) {
llvm::errs() << "Warning \"" << FilePath << "\" will not be transformed "
<< "because it's in the excluded list.\n";
return true;
}
return false;
}
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal();
Transforms TransformManager;
ReplacementHandling ReplacementHandler;
TransformManager.registerTransforms();
cl::HideUnrelatedOptions(llvm::makeArrayRef(VisibleCategories));
cl::SetVersionPrinter(&printVersion);
// Parse options and generate compilations.
std::unique_ptr<CompilationDatabase> Compilations(
FixedCompilationDatabase::loadFromCommandLine(argc, argv));
cl::ParseCommandLineOptions(argc, argv);
// Populate the ModifiableFiles structure.
GlobalOptions.ModifiableFiles.readListFromString(IncludePaths, ExcludePaths);
GlobalOptions.ModifiableFiles.readListFromFile(IncludeFromFile,
ExcludeFromFile);
if (!Compilations) {
std::string ErrorMessage;
Compilations = autoDetectCompilations(ErrorMessage);
if (!Compilations) {
llvm::errs() << llvm::sys::path::filename(argv[0]) << ": " << ErrorMessage
<< "\n";
return 1;
}
}
// Populate source files.
std::vector<std::string> Sources;
if (!SourcePaths.empty()) {
// Use only files that are not explicitly excluded.
std::remove_copy_if(SourcePaths.begin(), SourcePaths.end(),
std::back_inserter(Sources),
isFileExplicitlyExcludedPredicate);
} else {
if (GlobalOptions.ModifiableFiles.isIncludeListEmpty()) {
llvm::errs() << llvm::sys::path::filename(argv[0])
<< ": Use -include to indicate which files of "
<< "the compilatiion database to transform.\n";
return 1;
}
// Use source paths from the compilation database.
// We only transform files that are explicitly included.
Sources = Compilations->getAllFiles();
std::vector<std::string>::iterator E = std::remove_if(
Sources.begin(), Sources.end(), isFileNotIncludedPredicate);
Sources.erase(E, Sources.end());
}
if (Sources.empty()) {
llvm::errs() << llvm::sys::path::filename(argv[0])
<< ": Could not determine sources to transform.\n";
return 1;
}
// Enable timming.
GlobalOptions.EnableTiming = TimingDirectoryName.getNumOccurrences() > 0;
bool CmdSwitchError = false;
CompilerVersions RequiredVersions =
handleSupportedCompilers(argv[0], CmdSwitchError);
if (CmdSwitchError)
return 1;
TransformManager.createSelectedTransforms(GlobalOptions, RequiredVersions);
if (TransformManager.begin() == TransformManager.end()) {
if (SupportedCompilers.empty())
llvm::errs() << llvm::sys::path::filename(argv[0])
<< ": no selected transforms\n";
else
llvm::errs() << llvm::sys::path::filename(argv[0])
<< ": no transforms available for specified compilers\n";
return 1;
}
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts(
new DiagnosticOptions());
DiagnosticsEngine Diagnostics(
llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
DiagOpts.get());
// FIXME: Make this DiagnosticsEngine available to all Transforms probably via
// GlobalOptions.
// If SerializeReplacements is requested, then code reformatting must be
// turned off and only one transform should be requested.
if (SerializeOnly &&
(std::distance(TransformManager.begin(), TransformManager.end()) > 1 ||
DoFormat)) {
llvm::errs() << "Serialization of replacements requested for multiple "
"transforms.\nChanges from only one transform can be "
"serialized.\n";
return 1;
}
// If we're asked to apply changes to files on disk, need to locate
// clang-apply-replacements.
if (!SerializeOnly) {
if (!ReplacementHandler.findClangApplyReplacements(argv[0])) {
llvm::errs() << "Could not find clang-apply-replacements\n";
return 1;
}
if (DoFormat)
ReplacementHandler.enableFormatting(FormatStyleOpt, FormatStyleConfig);
}
StringRef TempDestinationDir;
if (SerializeLocation.getNumOccurrences() > 0)
ReplacementHandler.setDestinationDir(SerializeLocation);
else
TempDestinationDir = ReplacementHandler.useTempDestinationDir();
SourcePerfData PerfData;
for (Transforms::const_iterator I = TransformManager.begin(),
E = TransformManager.end();
I != E; ++I) {
Transform *T = *I;
if (T->apply(*Compilations, Sources) != 0) {
// FIXME: Improve ClangTool to not abort if just one file fails.
return 1;
}
if (GlobalOptions.EnableTiming)
collectSourcePerfData(*T, PerfData);
if (SummaryMode) {
llvm::outs() << "Transform: " << T->getName()
<< " - Accepted: " << T->getAcceptedChanges();
if (T->getChangesNotMade()) {
llvm::outs() << " - Rejected: " << T->getRejectedChanges()
<< " - Deferred: " << T->getDeferredChanges();
}
llvm::outs() << "\n";
}
if (!ReplacementHandler.serializeReplacements(T->getAllReplacements()))
return 1;
if (!SerializeOnly)
if (!ReplacementHandler.applyReplacements())
return 1;
}
// Let the user know which temporary directory the replacements got written
// to.
if (SerializeOnly && !TempDestinationDir.empty())
llvm::errs() << "Replacements serialized to: " << TempDestinationDir << "\n";
if (FinalSyntaxCheck) {
ClangTool SyntaxTool(*Compilations, SourcePaths);
if (SyntaxTool.run(newFrontendActionFactory<SyntaxOnlyAction>().get()) != 0)
return 1;
}
// Report execution times.
if (GlobalOptions.EnableTiming && !PerfData.empty()) {
std::string DirectoryName = TimingDirectoryName;
// Use default directory name.
if (DirectoryName.empty())
DirectoryName = "./migrate_perf";
writePerfDataJSON(DirectoryName, PerfData);
}
return 0;
}
// These anchors are used to force the linker to link the transforms
extern volatile int AddOverrideTransformAnchorSource;
extern volatile int LoopConvertTransformAnchorSource;
extern volatile int PassByValueTransformAnchorSource;
extern volatile int ReplaceAutoPtrTransformAnchorSource;
extern volatile int UseAutoTransformAnchorSource;
extern volatile int UseNullptrTransformAnchorSource;
static int TransformsAnchorsDestination[] = {
AddOverrideTransformAnchorSource,
LoopConvertTransformAnchorSource,
PassByValueTransformAnchorSource,
ReplaceAutoPtrTransformAnchorSource,
UseAutoTransformAnchorSource,
UseNullptrTransformAnchorSource
};

View File

@ -1,58 +0,0 @@
##===- tools/extra/loop-convert/Makefile ----sssss----------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
CLANG_LEVEL := ../../../..
include $(CLANG_LEVEL)/../../Makefile.config
TOOLNAME = clang-modernize
# No plugins, optimize startup time.
TOOL_NO_EXPORTS = 1
SOURCES = ClangModernize.cpp
# FIXME: All these gross relative paths will go away once transforms are lib-ified.
# For each Transform subdirectory add to SOURCES and BUILT_SOURCES.
# BUILT_SOURCES ensures a subdirectory is created to house object files from
# transform subdirectories. See below for more on .objdir.
SOURCES += $(addprefix ../LoopConvert/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../LoopConvert/*.cpp)))
BUILT_SOURCES = $(ObjDir)/../LoopConvert/.objdir
SOURCES += $(addprefix ../UseNullptr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../UseNullptr/*.cpp)))
BUILT_SOURCES += $(ObjDir)/../UseNullptr/.objdir
SOURCES += $(addprefix ../UseAuto/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../UseAuto/*.cpp)))
BUILT_SOURCES += $(ObjDir)/../UseAuto/.objdir
SOURCES += $(addprefix ../AddOverride/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../AddOverride/*.cpp)))
BUILT_SOURCES += $(ObjDir)/../AddOverride/.objdir
SOURCES += $(addprefix ../PassByValue/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../PassByValue/*.cpp)))
BUILT_SOURCES += $(ObjDir)/../PassByValue/.objdir
SOURCES += $(addprefix ../ReplaceAutoPtr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../ReplaceAutoPtr/*.cpp)))
BUILT_SOURCES += $(ObjDir)/../ReplaceAutoPtr/.objdir
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc mcparser option
USEDLIBS = modernizeCore.a clangFormat.a \
clangTooling.a clangToolingCore.a clangFrontend.a \
clangSerialization.a clangDriver.a clangRewriteFrontend.a \
clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \
clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
include $(CLANG_LEVEL)/Makefile
CPP.Flags += -I$(PROJ_SRC_DIR)/..
# BUILT_SOURCES gets used as a prereq for many top-level targets. However, at
# the point those targets are defined, $(ObjDir) hasn't been defined and so the
# directory to create becomes /<name>/ which is not what we want. So instead,
# this .objdir recipe is defined at at point where $(ObjDir) is defined and
# it's specialized to $(ObjDir) to ensure it only works on targets we want it
# to.
$(ObjDir)/%.objdir:
$(Verb) $(MKDIR) $(ObjDir)/$* > /dev/null
$(Verb) $(DOTDIR_TIMESTAMP_COMMAND) > $@

View File

@ -1,54 +0,0 @@
.. index:: Add-Override Transform
======================
Add-Override Transform
======================
The Add-Override Transform adds the ``override`` specifier to member
functions that override a virtual function in a base class and that
don't already have the specifier. The transform is enabled with the
:option:`-add-override` option of :program:`clang-modernize`.
For example:
.. code-block:: c++
class A {
public:
virtual void h() const;
};
class B : public A {
public:
void h() const;
// The declaration of h is transformed to
void h() const override;
};
Using Expands-to-Override Macros
================================
Like LLVM's ``LLVM_OVERRIDE``, several projects have macros that conditionally
expand to the ``override`` keyword when compiling with C++11 features enabled.
To maintain compatibility with non-C++11 builds, the Add-Override Transform
supports detection and use of these macros instead of using the ``override``
keyword directly. Specify ``-override-macros`` on the command line to the
Modernizer to enable this behavior.
Known Limitations
=================
* This transform will not insert the override keyword if a method is
pure. At the moment it's not possible to track down the pure
specifier location.
.. code-block:: c++
class B : public A {
public:
virtual void h() const = 0;
// The declaration of h is NOT transformed to
virtual void h() const override = 0;
};

View File

@ -1,257 +0,0 @@
.. index:: Loop Convert Transform
======================
Loop Convert Transform
======================
The Loop Convert Transform is a transformation to convert ``for(...; ...;
...)`` loops to use the new range-based loops in C++11. The transform is enabled
with the :option:`-loop-convert` option of :program:`clang-modernize`.
Three kinds of loops can be converted:
- Loops over statically allocated arrays
- Loops over containers, using iterators
- Loops over array-like containers, using ``operator[]`` and ``at()``
Risk
====
Risky
-----
In loops where the container expression is more complex than just a
reference to a declared expression (a variable, function, enum, etc.),
and some part of it appears elsewhere in the loop, we lower our confidence
in the transformation due to the increased risk of changing semantics.
Transformations for these loops are marked as `risky`, and thus will only
be converted if the acceptable risk level is set to ``-risk=risky``.
.. code-block:: c++
int arr[10][20];
int l = 5;
for (int j = 0; j < 20; ++j)
int k = arr[l][j] + l; // using l outside arr[l] is considered risky
for (int i = 0; i < obj.getVector().size(); ++i)
obj.foo(10); // using 'obj' is considered risky
See
:ref:`Range-based loops evaluate end() only once<IncorrectRiskyTransformation>`
for an example of an incorrect transformation when the maximum acceptable risk
level is set to `risky`.
Reasonable (Default)
--------------------
If a loop calls ``.end()`` or ``.size()`` after each iteration, the
transformation for that loop is marked as `reasonable`, and thus will
be converted if the acceptable risk level is set to ``-risk=reasonable``
(default) or higher.
.. code-block:: c++
// using size() is considered reasonable
for (int i = 0; i < container.size(); ++i)
cout << container[i];
Safe
----
Any other loops that do not match the above criteria to be marked as
`risky` or `reasonable` are marked `safe`, and thus will be converted
if the acceptable risk level is set to ``-risk=safe`` or higher.
.. code-block:: c++
int arr[] = {1,2,3};
for (int i = 0; i < 3; ++i)
cout << arr[i];
Example
=======
Original:
.. code-block:: c++
const int N = 5;
int arr[] = {1,2,3,4,5};
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
// safe transform
for (int i = 0; i < N; ++i)
cout << arr[i];
// reasonable transform
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
cout << *it;*
// reasonable transform
for (int i = 0; i < v.size(); ++i)
cout << v[i];
After transformation with risk level set to ``-risk=reasonable`` (default):
.. code-block:: c++
const int N = 5;
int arr[] = {1,2,3,4,5};
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
// safe transform
for (auto & elem : arr)
cout << elem;
// reasonable transform
for (auto & elem : v)
cout << elem;
// reasonable transform
for (auto & elem : v)
cout << elem;
Limitations
===========
There are certain situations where the tool may erroneously perform
transformations that remove information and change semantics. Users of the tool
should be aware of the behaviour and limitations of the transform outlined by
the cases below.
Comments inside loop headers
----------------------------
Comments inside the original loop header are ignored and deleted when
transformed.
.. code-block:: c++
for (int i = 0; i < N; /* This will be deleted */ ++i) { }
Range-based loops evaluate end() only once
------------------------------------------
The C++11 range-based for loop calls ``.end()`` only once during the
initialization of the loop. If in the original loop ``.end()`` is called after
each iteration the semantics of the transformed loop may differ.
.. code-block:: c++
// The following is semantically equivalent to the C++11 range-based for loop,
// therefore the semantics of the header will not change.
for (iterator it = container.begin(), e = container.end(); it != e; ++it) { }
// Instead of calling .end() after each iteration, this loop will be
// transformed to call .end() only once during the initialization of the loop,
// which may affect semantics.
for (iterator it = container.begin(); it != container.end(); ++it) { }
.. _IncorrectRiskyTransformation:
As explained above, calling member functions of the container in the body
of the loop is considered `risky`. If the called member function modifies the
container the semantics of the converted loop will differ due to ``.end()``
being called only once.
.. code-block:: c++
bool flag = false;
for (vector<T>::iterator it = vec.begin(); it != vec.end(); ++it) {
// Add a copy of the first element to the end of the vector.
if (!flag) {
// This line makes this transformation 'risky'.
vec.push_back(*it);
flag = true;
}
cout << *it;
}
The original code above prints out the contents of the container including the
newly added element while the converted loop, shown below, will only print the
original contents and not the newly added element.
.. code-block:: c++
bool flag = false;
for (auto & elem : vec) {
// Add a copy of the first element to the end of the vector.
if (!flag) {
// This line makes this transformation 'risky'
vec.push_back(elem);
flag = true;
}
cout << elem;
}
Semantics will also be affected if ``.end()`` has side effects. For example, in
the case where calls to ``.end()`` are logged the semantics will change in the
transformed loop if ``.end()`` was originally called after each iteration.
.. code-block:: c++
iterator end() {
num_of_end_calls++;
return container.end();
}
Overloaded operator->() with side effects
-----------------------------------------
Similarly, if ``operator->()`` was overloaded to have side effects, such as
logging, the semantics will change. If the iterator's ``operator->()`` was used
in the original loop it will be replaced with ``<container element>.<member>``
instead due to the implicit dereference as part of the range-based for loop.
Therefore any side effect of the overloaded ``operator->()`` will no longer be
performed.
.. code-block:: c++
for (iterator it = c.begin(); it != c.end(); ++it) {
it->func(); // Using operator->()
}
// Will be transformed to:
for (auto & elem : c) {
elem.func(); // No longer using operator->()
}
Pointers and references to containers
-------------------------------------
While most of the transform's risk analysis is dedicated to determining whether
the iterator or container was modified within the loop, it is possible to
circumvent the analysis by accessing and modifying the container through a
pointer or reference.
If the container were directly used instead of using the pointer or reference
the following transformation would have only been applied at the ``-risk=risky``
level since calling a member function of the container is considered `risky`.
The transform cannot identify expressions associated with the container that are
different than the one used in the loop header, therefore the transformation
below ends up being performed at the ``-risk=safe`` level.
.. code-block:: c++
vector<int> vec;
vector<int> *ptr = &vec;
vector<int> &ref = vec;
for (vector<int>::iterator it = vec.begin(), e = vec.end(); it != e; ++it) {
if (!flag) {
// Accessing and modifying the container is considered risky, but the risk
// level is not raised here.
ptr->push_back(*it);
ref.push_back(*it);
flag = true;
}
}

View File

@ -1,6 +0,0 @@
===================
cpp11-migrate Usage
===================
This program has been renamed :doc:`clang-modernize <clang-modernize>`, and its usage is now
found in :doc:`ModernizerUsage`.

View File

@ -1,313 +0,0 @@
=====================
clang-modernize Usage
=====================
``clang-modernize [options] [<sources>...] [-- [args]]``
``<source#>`` specifies the path to the source to migrate. This path may be
relative to the current directory. If no sources are provided, a compilation
database provided with `-p`_ can be used to provide sources together with the
`include/exclude options`_.
By default all transformations are applied. There are two ways to enable a
subset of the transformations:
1. Explicitly, by referring to the transform options directly, see
:ref:`transform-specific-command-line-options`.
2. Implicitly, based on the compilers to support, see
:ref:`-for-compilers=\<string\> <for-compilers-option>`.
If both ways of specifying transforms are used only explicitly specified
transformations that are supported by the given compilers will be applied.
General Command Line Options
============================
.. option:: -help
Displays tool usage instructions and command line options.
.. option:: -version
Displays the version information of this tool.
.. _-p:
.. option:: -p=<build-path>
``<build-path>`` is the directory containing a *compilation databasefile*, a
file named ``compile_commands.json``, which provides compiler arguments for
building each source file. CMake can generate this file by specifying
``-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`` when running CMake. Ninja_, since v1.2
can also generate this file with ``ninja -t compdb``. If the compilation
database cannot be used for any reason, an error is reported.
This option is ignored if ``--`` is present.
Files in the compilation database will be transformed if no sources are
provided and paths to files are explicitly included using ``-include`` or
``-include-from``.
In order to transform all files in a compilation database the following
command line can be used:
``clang-modernize -p=<build-path> -include=<project_root>``
Use ``-exclude`` or ``-exclude-from`` to limit the scope of ``-include``.
.. _Ninja: http://martine.github.io/ninja/
.. option:: -- [args]
Another way to provide compiler arguments is to specify all arguments on the
command line following ``--``. Arguments provided this way are used for
*every* source file.
If neither ``--`` nor ``-p`` are specified a compilation database is
searched for starting with the path of the first-provided source file and
proceeding through parent directories. If no compilation database is found or
one is found and cannot be used for any reason then ``-std=c++11`` is used as
the only compiler argument.
.. option:: -risk=<risk-level>
Some transformations may cause a change in semantics. In such cases the
maximum acceptable risk level specified through the ``-risk`` command
line option decides whether or not a transformation is applied.
Three different risk level options are available:
``-risk=safe``
Perform only safe transformations.
``-risk=reasonable`` (default)
Enable transformations that may change semantics.
``-risk=risky``
Enable transformations that are likely to change semantics.
The meaning of risk is handled differently for each transform. See
:ref:`transform documentation <transforms>` for details.
.. option:: -final-syntax-check
After applying the final transform to a file, parse the file to ensure the
last transform did not introduce syntax errors. Syntax errors introduced by
earlier transforms are already caught when subsequent transforms parse the
file.
.. option:: -summary
Displays a summary of the number of changes each transform made or could have
made to each source file immediately after each transform is applied.
**Accepted** changes are those actually made. **Rejected** changes are those
that could have been made if the acceptable risk level were higher.
**Deferred** changes are those that might be possible but they might conflict
with other accepted changes. Re-applying the transform will resolve deferred
changes.
.. _for-compilers-option:
.. option:: -for-compilers=<string>
Select transforms targeting the intersection of language features supported by
the given compilers.
Four compilers are supported. The transforms are enabled according to this
table:
=============== ===== === ==== ====
Transforms clang gcc icc mscv
=============== ===== === ==== ====
AddOverride (1) 3.0 4.7 14 8
LoopConvert 3.0 4.6 13 11
PassByValue 3.0 4.6 13 11
ReplaceAutoPtr 3.0 4.6 13 11
UseAuto 2.9 4.4 12 10
UseNullptr 3.0 4.6 12.1 10
=============== ===== === ==== ====
(1): if *-override-macros* is provided it's assumed that the macros are C++11
aware and the transform is enabled without regard to the supported compilers.
The structure of the argument to the `-for-compilers` option is
**<compiler>-<major ver>[.<minor ver>]** where **<compiler>** is one of the
compilers from the above table.
Some examples:
1. To support `Clang >= 3.0`, `gcc >= 4.6` and `MSVC >= 11`:
``clang-modernize -for-compilers=clang-3.0,gcc-4.6,msvc-11 <args..>``
Enables LoopConvert, ReplaceAutoPtr, UseAuto, UseNullptr.
2. To support `icc >= 12` while using a C++11-aware macro for the `override`
virtual specifier:
``clang-modernize -for-compilers=icc-12 -override-macros <args..>``
Enables AddOverride and UseAuto.
.. warning::
If your version of Clang depends on the GCC headers (e.g: when `libc++` is
not used), then you probably want to add the GCC version to the targeted
platforms as well.
.. option:: -perf[=<directory>]
Turns on performance measurement and output functionality. The time it takes to
apply each transform is recorded by the migrator and written in JSON format
to a uniquely named file in the given ``<directory>``. All sources processed
by a single Modernizer process are written to the same output file. If
``<directory>`` is not provided the default is ``./migrate_perf/``.
The time recorded for a transform includes parsing and creating source code
replacements.
.. option:: -serialize-replacements
Causes the modernizer to generate replacements and serialize them to disk but
not apply them. This can be useful for debugging or for manually running
``clang-apply-replacements``. Replacements are serialized in YAML_ format.
By default serialzied replacements are written to a temporary directory whose
name is written to stderr when serialization is complete.
.. _YAML: http://www.yaml.org/
.. option:: -serialize-dir=<string>
Choose a directory to serialize replacements to. The directory must exist.
.. _include/exclude options:
Path Inclusion/Exclusion Options
================================
.. option:: -include=<path1>,<path2>,...,<pathN>
Use this option to indicate which directories contain files that can be
changed by the modernizer. Inidividual files may be specified if desired.
Multiple paths can be specified as a comma-separated list. Sources mentioned
explicitly on the command line are always included so this option controls
which other files (e.g. headers) may be changed while transforming
translation units.
.. option:: -exclude=<path1>,<path2>,...,<pathN>
Used with ``-include`` to provide finer control over which files and
directories can be transformed. Individual files and files within directories
specified by this option **will not** be transformed. Multiple paths can be
specified as a comma-separated list.
.. option:: -include-from=<filename>
Like ``-include`` but read paths from the given file. Paths should be one per
line.
.. option:: -exclude-from=<filename>
Like ``-exclude`` but read paths from the given file. Paths are listed one
per line.
Formatting Command Line Options
===============================
.. option:: -format
Enable reformatting of code changed by transforms. Formatting is done after
every transform.
.. option:: -style=<string>
Specifies how formatting should be done. The behaviour of this option is
identical to the same option provided by clang-format_. Refer to
`clang-format's style options`_ for more details.
.. option:: -style-config=<dir>
When using ``-style=file``, the default behaviour is to look for
``.clang-format`` starting in the current directory and then in ancestors. To
specify a directory to find the style configuration file, use this option.
Example:
.. code-block:: c++
:emphasize-lines: 10-12,18
// file.cpp
for (std::vector<int>::const_iterator I = my_container.begin(),
E = my_container.end();
I != E; ++I) {
std::cout << *I << std::endl;
}
// No reformatting:
// clang-modernize -use-auto file.cpp
for (auto I = my_container.begin(),
E = my_container.end();
I != E; ++I) {
std::cout << *I << std::endl;
}
// With reformatting enabled:
// clang-modernize -format -use-auto file.cpp
for (auto I = my_container.begin(), E = my_container.end(); I != E; ++I) {
std::cout << *I << std::endl;
}
.. _clang-format: http://clang.llvm.org/docs/ClangFormat.html
.. _clang-format's style options: http://clang.llvm.org/docs/ClangFormatStyleOptions.html
.. _transform-specific-command-line-options:
Transform-Specific Command Line Options
=======================================
.. option:: -loop-convert
Makes use of C++11 range-based for loops where possible. See
:doc:`LoopConvertTransform`.
.. option:: -use-nullptr
Makes use of the new C++11 keyword ``nullptr`` where possible.
See :doc:`UseNullptrTransform`.
.. option:: -user-null-macros=<string>
``<string>`` is a comma-separated list of user-defined macros that behave like
the ``NULL`` macro. The :option:`-use-nullptr` transform will replace these
macros along with ``NULL``. See :doc:`UseNullptrTransform`.
.. option:: -use-auto
Replace the type specifier of variable declarations with the ``auto`` type
specifier. See :doc:`UseAutoTransform`.
.. option:: -add-override
Adds the override specifier to member functions where it is appropriate. That
is, the override specifier is added to member functions that override a
virtual function in a base class and that don't already have the specifier.
See :doc:`AddOverrideTransform`.
.. option:: -override-macros
Tells the Add Override Transform to locate a macro that expands to
``override`` and use that macro instead of the ``override`` keyword directly.
If no such macro is found, ``override`` is still used. This option enables
projects that use such macros to maintain build compatibility with non-C++11
code.
.. option:: -pass-by-value
Replace const-reference parameters by values in situations where it can be
beneficial.
See :doc:`PassByValueTransform`.
.. option:: -replace-auto_ptr
Replace ``std::auto_ptr`` (deprecated in C++11) by ``std::unique_ptr`` and
wrap calls to the copy constructor and assignment operator with
``std::move()``.
See :doc:`ReplaceAutoPtrTransform`.

View File

@ -1,165 +0,0 @@
.. index:: Pass-By-Value Transform
=======================
Pass-By-Value Transform
=======================
The Pass-By-Value Transform makes use of the pass-by-value idiom when possible.
With move semantics added to the language and the standard library updated with
move constructors added for many types it is now interesting to take an argument
directly by value, instead of by const-reference, and then copy. This
transformation allows the compiler to take care of choosing the best way to
construct the copy.
The transformation is usually beneficial when the calling code passes an
*rvalue* and assumes the move construction is a cheap operation. This short
example illustrates how the construction of the value happens:
.. code-block:: c++
void foo(std::string s);
std::string get_str();
void f(const std::string &str) {
foo(str); // lvalue -> copy construction
foo(get_str()); // prvalue -> move construction
}
.. note::
Currently only constructors are transformed to make use of pass-by-value.
Contributions that handle other situations are welcome!
Pass-by-value in constructors
-----------------------------
Replaces the uses of const-references constructor parameters that are copied
into class fields. The parameter is then moved with `std::move()`.
Since `std::move()` is a library function declared in `<utility>` it may be
necessary to add this include. The transform will add the include directive when
necessary.
Example::
$ clang-modernize -pass-by-value ctor.cpp
**ctor.cpp**
.. code-block:: c++
#include <string>
class Foo {
public:
- Foo(const std::string &Copied, const std::string &ReadOnly)
- : Copied(Copied), ReadOnly(ReadOnly)
+ Foo(std::string Copied, const std::string &ReadOnly)
+ : Copied(std::move(Copied)), ReadOnly(ReadOnly)
{}
private:
std::string Copied;
const std::string &ReadOnly;
};
std::string get_cwd();
void f(const std::string &Path) {
// The parameter corresponding to 'get_cwd()' is move-constructed. By
// using pass-by-value in the Foo constructor we managed to avoid a
// copy-construction.
Foo foo(get_cwd(), Path);
}
If the parameter is used more than once no transformation is performed since
moved objects have an undefined state. It means the following code will be left
untouched:
.. code-block:: c++
#include <string>
void pass(const std::string &S);
struct Foo {
Foo(const std::string &S) : Str(S) {
pass(S);
}
std::string Str;
};
Risk
^^^^
This modification is considered **reasonably safe** (see :option:`-risk`
option).
A situation where the generated code can be wrong is when the object referenced
is modified before the assignment in the init-list through a "hidden" reference.
Example:
.. code-block:: c++
std::string s("foo");
struct Base {
Base() {
s = "bar";
}
};
struct Derived : Base {
- Derived(const std::string &S) : Field(S)
+ Derived(std::string S) : Field(std::move(S))
{ }
std::string Field;
};
void f() {
- Derived d(s); // d.Field holds "bar"
+ Derived d(s); // d.Field holds "foo"
}
Note about delayed template parsing
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When delayed template parsing is enabled, constructors part of templated
contexts; templated constructors, constructors in class templates, constructors
of inner classes of template classes, etc., are not transformed. Delayed
template parsing is enabled by default on Windows as a Microsoft extension:
`Clang Compiler Users Manual - Microsoft extensions`_.
Delayed template parsing can be enabled using the `-fdelayed-template-parsing`
flag and disabled using `-fno-delayed-template-parsing`.
Example:
.. code-block:: c++
template <typename T> class C {
std::string S;
public:
= // using -fdelayed-template-parsing (default on Windows)
= C(const std::string &S) : S(S) {}
+ // using -fno-delayed-template-parsing (default on non-Windows systems)
+ C(std::string S) : S(std::move(S)) {}
};
.. _Clang Compiler Users Manual - Microsoft extensions: http://clang.llvm.org/docs/UsersManual.html#microsoft-extensions
.. seealso::
For more information about the pass-by-value idiom, read: `Want Speed? Pass by Value`_.
.. _Want Speed? Pass by Value: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

View File

@ -1,72 +0,0 @@
.. index:: Replace-AutoPtr Transform
=========================
Replace-AutoPtr Transform
=========================
The Replace-AutoPtr Transform replaces the uses of the deprecated class
``std::auto_ptr`` by ``std::unique_ptr`` (introduced in C++11). The transfer of
ownership, done by the copy-constructor and the assignment operator, is changed
to match ``std::unique_ptr`` usage by using explicit calls to ``std::move()``.
The transform is enabled with the :option:`-replace-auto_ptr` option of
:program:`clang-modernize`.
Migration example:
.. code-block:: c++
-void take_ownership_fn(std::auto_ptr<int> int_ptr);
+void take_ownership_fn(std::unique_ptr<int> int_ptr);
void f(int x) {
- std::auto_ptr<int> a(new int(x));
- std::auto_ptr<int> b;
+ std::unique_ptr<int> a(new int(x));
+ std::unique_ptr<int> b;
- b = a;
- take_ownership_fn(b);
+ b = std::move(a);
+ take_ownership_fn(std::move(b));
}
Known Limitations
=================
* If headers modification is not activated or if a header is not allowed to be
changed this transform will produce broken code (compilation error), where the
the headers' code will stay unchanged while the code using them will be
changed.
* Client code that declares a reference to an ``std::auto_ptr`` coming from code
that can't be migrated (such as a header coming from a 3\ :sup:`rd` party
library) will produce a compilation error after migration. This is because the
type of the reference will be changed to ``std::unique_ptr`` but the type
returned by the library won't change, binding a reference to
``std::unique_ptr`` from an ``std::auto_ptr``. This pattern doesn't make much
sense and usually ``std::auto_ptr`` are stored by value (otherwise what is the
point in using them instead of a reference or a pointer?).
.. code-block:: c++
// <3rd-party header...>
std::auto_ptr<int> get_value();
const std::auto_ptr<int> & get_ref();
// <calling code (with migration)...>
-std::auto_ptr<int> a(get_value());
+std::unique_ptr<int> a(get_value()); // ok, unique_ptr constructed from auto_ptr
-const std::auto_ptr<int> & p = get_ptr();
+const std::unique_ptr<int> & p = get_ptr(); // won't compile
* Non-instantiated templates aren't modified.
.. code-block:: c++
template <typename X>
void f() {
std::auto_ptr<X> p;
}
// only 'f<int>()' (or similar) will trigger the replacement

View File

@ -1,137 +0,0 @@
.. index:: Use-Auto Transform
==================
Use-Auto Transform
==================
The Use-Auto Transform is responsible for using the ``auto`` type specifier for
variable declarations to *improve code readability and maintainability*. The
transform is enabled with the :option:`-use-auto` option of
:program:`clang-modernize`. For example:
.. code-block:: c++
std::vector<int>::iterator I = my_container.begin();
// transforms to:
auto I = my_container.begin();
The ``auto`` type specifier will only be introduced in situations where the
variable type matches the type of the initializer expression. In other words
``auto`` should deduce the same type that was originally spelled in the source.
However, not every situation should be transformed:
.. code-block:: c++
int val = 42;
InfoStruct &I = SomeObject.getInfo();
// Should not become:
auto val = 42;
auto &I = SomeObject.getInfo();
In this example using ``auto`` for builtins doesn't improve readability. In
other situations it makes the code less self-documenting impairing readability
and maintainability. As a result, ``auto`` is used only introduced in specific
situations described below.
Iterators
=========
Iterator type specifiers tend to be long and used frequently, especially in
loop constructs. Since the functions generating iterators have a common format,
the type specifier can be replaced without obscuring the meaning of code while
improving readability and maintainability.
.. code-block:: c++
for (std::vector<int>::iterator I = my_container.begin(),
E = my_container.end();
I != E; ++I) {
}
// becomes
for (auto I = my_container.begin(), E = my_container.end(); I != E; ++I) {
}
The transform will only replace iterator type-specifiers when all of the
following conditions are satisfied:
* The iterator is for one of the standard container in ``std`` namespace:
* ``array``
* ``deque``
* ``forward_list``
* ``list``
* ``vector``
* ``map``
* ``multimap``
* ``set``
* ``multiset``
* ``unordered_map``
* ``unordered_multimap``
* ``unordered_set``
* ``unordered_multiset``
* ``queue``
* ``priority_queue``
* ``stack``
* The iterator is one of the possible iterator types for standard containers:
* ``iterator``
* ``reverse_iterator``
* ``const_iterator``
* ``const_reverse_iterator``
* In addition to using iterator types directly, typedefs or other ways of
referring to those types are also allowed. However, implementation-specific
types for which a type like ``std::vector<int>::iterator`` is itself a
typedef will not be transformed. Consider the following examples:
.. code-block:: c++
// The following direct uses of iterator types will be transformed.
std::vector<int>::iterator I = MyVec.begin();
{
using namespace std;
list<int>::iterator I = MyList.begin();
}
// The type specifier for J would transform to auto since it's a typedef
// to a standard iterator type.
typedef std::map<int, std::string>::const_iterator map_iterator;
map_iterator J = MyMap.begin();
// The following implementation-specific iterator type for which
// std::vector<int>::iterator could be a typedef would not be transformed.
__gnu_cxx::__normal_iterator<int*, std::vector> K = MyVec.begin();
* The initializer for the variable being declared is not a braced initializer
list. Otherwise, use of ``auto`` would cause the type of the variable to be
deduced as``std::initializer_list``.
Known Limitations
=================
* If the initializer is an explicit conversion constructor, the transform will
not replace the type specifier even though it would be safe to do so.
* User-defined iterators are not handled at this time.

View File

@ -1,82 +0,0 @@
.. index:: Use-Nullptr Transform
=====================
Use-Nullptr Transform
=====================
The Use-Nullptr Transform is a transformation to convert the usage of null
pointer constants (eg. ``NULL``, ``0``) to use the new C++11 ``nullptr``
keyword. The transform is enabled with the :option:`-use-nullptr` option of
:program:`clang-modernize`.
Example
=======
.. code-block:: c++
void assignment() {
char *a = NULL;
char *b = 0;
char c = 0;
}
int *ret_ptr() {
return 0;
}
transforms to:
.. code-block:: c++
void assignment() {
char *a = nullptr;
char *b = nullptr;
char c = 0;
}
int *ret_ptr() {
return nullptr;
}
User defined macros
===================
By default this transform will only replace the ``NULL`` macro and will skip any
user-defined macros that behaves like ``NULL``. The user can use the
:option:`-user-null-macros` option to specify a comma-separated list of macro
names that will be transformed along with ``NULL``.
Example
-------
.. code-block:: c++
#define MY_NULL (void*)0
void assignment() {
void *p = MY_NULL;
}
using the command-line
.. code-block:: bash
clang-modernize -use-nullptr -user-null-macros=MY_NULL foo.cpp
transforms to:
.. code-block:: c++
#define MY_NULL NULL
void assignment() {
int *p = nullptr;
}
Risk
====
:option:`-risk` has no effect in this transform.

View File

@ -1,112 +1,2 @@
.. index:: clang-modernize
.. note::
**Deprecation**
As of September 2015 all :program:`clang-modernize` transforms have been
ported to :doc:`clang-tidy/index`. :program:`clang-modernize` is deprecated
and is going to be removed soon.
==================================
Clang C++ Modernizer User's Manual
==================================
.. toctree::
:hidden:
UseAutoTransform
UseNullptrTransform
LoopConvertTransform
AddOverrideTransform
PassByValueTransform
ReplaceAutoPtrTransform
ModernizerUsage
cpp11-migrate
MigratorUsage
:program:`clang-modernize` is a standalone tool used to automatically convert
C++ code written against old standards to use features of the newest C++
standard where appropriate.
Getting Started
===============
To build from source:
1. Read `Getting Started with the LLVM System`_ and `Clang Tools
Documentation`_ for information on getting sources for LLVM, Clang, and
Clang Extra Tools.
2. `Getting Started with the LLVM System`_ and `Building LLVM with CMake`_ give
directions for how to build. With sources all checked out into the
right place the LLVM build will build Clang Extra Tools and their
dependencies automatically.
* If using CMake, you can also use the ``clang-modernize`` target to build
just the Modernizer and its dependencies.
Before continuing, take a look at :doc:`ModernizerUsage` to see how to invoke
the Modernizer.
Before running the Modernizer on code you'll need the arguments you'd normally
pass to the compiler. If you're migrating a single file with few compiler
arguments, it might be easier to pass the compiler args on the command line
after ``--``. If you don't have any compiler arguments then ``--`` is not needed.
If you're working with multiple files or even a single file with many compiler
args, it's probably best to use a *compilation database*.
A `compilation database`_ contains the command-line arguments for multiple
files. If the code you want to transform can be built with CMake, you can
generate this database easily by running CMake with the
``-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`` option. The Ninja_ build system, since
v1.2, can create this file too using the *compdb* tool: ``ninja -t compdb``. If
you're not already using either of these tools or cannot easily make use of
them you might consider looking into Bear_.
In addition to the compiler arguments you usually build your code with, you must
provide the option for enabling C++11 features. For clang and versions of gcc
≥ v4.8 this is ``-std=c++11``.
With compiler arguments in hand, the modernizer can be applied to sources. Each
transform is applied to all sources before the next transform. All the changes
generated by each transform pass are serialized to disk and applied using
``clang-apply-replacements``. This executable must be located on the ``PATH``
or be present in the same directory as the ``clang-modernizer`` executable. If
any changes fail to apply, the modernizer will **not** proceed to the next
transform and will halt.
There's a small chance that changes made by a transform will produce code that
doesn't compile, also causing the modernizer to halt. This can happen with
bugs in the transforms or use of the pre-processor to make the same code behave
differently between translation units. Before logging a bug, be sure which
situation you are dealing with.
.. _Ninja: http://martine.github.io/ninja/
.. _Bear: https://github.com/rizsotto/Bear
.. _compilation database: http://clang.llvm.org/docs/JSONCompilationDatabase.html
.. _Getting Started with the LLVM System: http://llvm.org/docs/GettingStarted.html
.. _Building LLVM with CMake: http://llvm.org/docs/CMake.html
.. _Clang Tools Documentation: http://clang.llvm.org/docs/ClangTools.html
.. _transforms:
Transformations
===============
The Modernizer is a collection of independent transforms which can be
independently enabled. The transforms currently implemented are:
* :doc:`LoopConvertTransform`
* :doc:`UseNullptrTransform`
* :doc:`UseAutoTransform`
* :doc:`AddOverrideTransform`
* :doc:`PassByValueTransform`
* :doc:`ReplaceAutoPtrTransform`
All :program:`clang-modernize` transforms have moved to :doc:`clang-tidy/index`
(see the ``modernize`` module).

View File

@ -1,5 +1,2 @@
============================
C++11 Migrator User's Manual
============================
This tool has been renamed :doc:`clang-modernize <clang-modernize>`.
All :program:`clang-modernize` transforms have moved to :doc:`clang-tidy/index`
(see the ``modernize`` module).

View File

@ -15,7 +15,6 @@ Contents
.. toctree::
:maxdepth: 1
clang-modernize
clang-tidy/index
modularize
pp-trace

View File

@ -36,7 +36,6 @@ set(CLANG_TOOLS_TEST_DEPS
# Individual tools we test.
clang-apply-replacements
clang-modernize
clang-rename
clang-query
clang-tidy

View File

@ -1,161 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: clang-modernize -add-override %t.cpp -- -I %S -std=c++11
// RUN: FileCheck -input-file=%t.cpp %s
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: clang-modernize -add-override -override-macros %t.cpp -- -I %S -std=c++11
// RUN: FileCheck --check-prefix=MACRO --input-file=%t.cpp %s
struct A {
virtual ~A();
// CHECK: virtual ~A();
void f();
virtual void h() const;
// CHECK: virtual void h() const;
virtual void i() = 0;
// CHECK: virtual void i() = 0;
};
// Test that override isn't added to non-virtual functions.
struct B : public A {
void f();
// CHECK: struct B
// CHECK-NEXT: void f();
};
// Test that override is added to functions that override virtual functions.
struct C : public A {
void h() const;
// CHECK: struct C
// CHECK-NEXT: void h() const override;
// MACRO: struct C
// MACRO-NEXT: void h() const override;
};
// Test that override isn't add to functions that overload but not override.
struct D : public A {
void h();
// CHECK: struct D
// CHECK-NEXT: void h();
};
// Test that override isn't added again to functions that already have it.
struct E : public A {
void h() const override;
// CHECK: struct E
// CHECK-NEXT: void h() const override;
// MACRO: struct E
// MACRO-NEXT: void h() const override;
};
// Test that override isn't added to the destructor.
struct F : public A {
virtual ~F();
// CHECK: struct F
// CHECK-NEXT: virtual ~F();
};
// Test that override is placed before any end of line comments.
struct G : public A {
void h() const; // comment
void i() // comment
{}
// CHECK: struct G
// CHECK-NEXT: void h() const override; // comment
// CHECK-NEXT: void i() override // comment
// CHECK-NEXT: {}
};
// Test that override is placed correctly if there is an inline body.
struct H : public A {
void h() const { }
// CHECK: struct H
// CHECK-NEXT: void h() const override { }
};
// Test that override is placed correctly if there is a body on the next line.
struct I : public A {
void h() const
{ }
// CHECK: struct I
// CHECK-NEXT: void h() const override
// CHECK-NEXT: { }
};
// Test that override is placed correctly if there is a body outside the class.
struct J : public A {
void h() const;
// CHECK: struct J
// CHECK-NEXT: void h() const override;
};
void J::h() const {
// CHECK: void J::h() const {
}
// Test that override is placed correctly if there is a trailing return type.
struct K : public A {
auto h() const -> void;
// CHECK: struct K
// CHECK-NEXT: auto h() const -> void override;
};
#define LLVM_OVERRIDE override
// Test that override isn't added if it is already specified via a macro.
struct L : public A {
void h() const LLVM_OVERRIDE;
// CHECK: struct L
// CHECK-NEXT: void h() const LLVM_OVERRIDE;
// MACRO: struct L
// MACRO-NEXT: void h() const LLVM_OVERRIDE;
};
template <typename T>
struct M : public A {
virtual void i();
// CHECK: struct M
// CHECK-NEXT: virtual void i() override;
// MACRO: struct M
// MACRO-NEXT: virtual void i() LLVM_OVERRIDE;
};
M<int> b;
// Test that override isn't added at the wrong place for "pure overrides"
struct APure {
virtual APure *clone() = 0;
};
struct BPure : APure {
virtual BPure *clone() { return new BPure(); }
};
struct CPure : BPure {
virtual BPure *clone() = 0;
// CHECK: struct CPure : BPure {
// CHECK-NOT: virtual BPure *clone() = 0 override;
// CHECK: };
};
struct DPure : CPure {
virtual DPure *clone() { return new DPure(); }
};
// Test that override is not added on dangerous template constructs
struct Base1 {
virtual void f();
};
struct Base2 {};
template<typename T> struct Derived : T {
void f(); // adding 'override' here will break instantiation of Derived<Base2>
// CHECK: struct Derived
// CHECK-NEXT: void f();
};
Derived<Base1> d1;
Derived<Base2> d2;
#undef LLVM_OVERRIDE
struct N : public A {
void h() const;
// CHECK: struct N
// CHECK-NEXT: void h() const override;
// MACRO: struct N
// MACRO-NEXT: void h() const override;
};

View File

@ -1,20 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: clang-modernize -add-override %t.cpp -- -I %S
// RUN: FileCheck -input-file=%t.cpp %s
// XFAIL: *
// Test that override isn't placed correctly after "pure overrides"
struct A {
virtual A *clone() = 0;
};
struct B : A {
virtual B *clone() { return new B(); }
};
struct C : B {
virtual B *clone() = 0;
// CHECK: struct C : B {
// CHECK: virtual B *clone() override = 0;
};
struct D : C {
virtual D *clone() { return new D(); }
};

View File

@ -1,45 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t_risky.cpp
// RUN: clang-modernize -loop-convert -use-nullptr %t.cpp -- -std=c++11
// RUN: FileCheck -input-file=%t.cpp %s
// RUN: clang-modernize -loop-convert -use-nullptr -risk=risky %t_risky.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=RISKY -input-file=%t_risky.cpp %s
#define NULL 0
struct T {
struct iterator {
int *& operator*();
const int *& operator*() const;
iterator & operator++();
bool operator!=(const iterator &other);
void insert(int *);
int *x;
};
iterator begin();
iterator end();
};
void test_loopconvert_and_nullptr_iterator() {
T t;
for (T::iterator it = t.begin(); it != t.end(); ++it) {
*it = NULL;
}
// CHECK: for (auto & elem : t)
// CHECK-NEXT: elem = nullptr;
}
void test_loopconvert_and_nullptr_risky() {
const int N = 10;
int *(*pArr)[N];
for (int i = 0; i < N; ++i) {
(*pArr)[i] = NULL;
}
// RISKY: for (auto & elem : *pArr)
// RISKY-NEXT: elem = nullptr;
}

View File

@ -1,63 +0,0 @@
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: clang-modernize -for-compilers=clang-2.9 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=CLANG-29 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: clang-modernize -for-compilers=clang-2.9 -override-macros %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=CLANG-29-OV-MACROS -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: clang-modernize -for-compilers=clang-3.0 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=CLANG-30 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: clang-modernize -for-compilers=gcc-4.6 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=GCC-46 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: clang-modernize -for-compilers=gcc-4.7 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=GCC-47 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: clang-modernize -for-compilers=icc-13 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=ICC-13 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: clang-modernize -for-compilers=icc-14 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=ICC-14 -input-file=%t.cpp %s
//
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: clang-modernize -for-compilers=msvc-8 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=MSVC-8 -input-file=%t.cpp %s
//
// Test multiple compilers
// RUN: grep -Ev "// *[A-Z0-9-]+:" %s > %t.cpp
// RUN: clang-modernize -for-compilers=clang-3.0,gcc-4.6,gcc-4.7 %t.cpp -- -std=c++11
// RUN: FileCheck -check-prefix=MULTIPLE -input-file=%t.cpp %s
//
// Test unknown platform
// RUN: not clang-modernize -for-compilers=foo-10 %t.cpp -- -std=c++11
//
// Test when no transforms can be selected because the compiler lacks support of
// the needed C++11 features
// RUN: not clang-modernize -for-compilers=clang-2.0 %t.cpp -- -std=c++11
// Test add overrides
struct A {
virtual A *clone() = 0;
};
#define LLVM_OVERRIDE override
struct B : A {
virtual B *clone();
// CLANG-29-OV-MACROS: virtual B *clone() LLVM_OVERRIDE;
// CLANG-29: virtual B *clone();
// CLANG-30: virtual B *clone() override;
// GCC-46: virtual B *clone();
// GCC-47: virtual B *clone() override;
// ICC-13: virtual B *clone();
// ICC-14: virtual B *clone() override;
// MSVC-8: virtual B *clone() override;
// MULTIPLE: virtual B *clone();
};

View File

@ -1,3 +0,0 @@
void foo() {
int *p = 0;
}

View File

@ -1,3 +0,0 @@
void foo() {
int *p = nullptr;
}

View File

@ -1,17 +0,0 @@
[
{
"directory": "$(path)/a1/",
"command": "clang++ -o compilations.o -c $(path)/a1/compilations.cpp -std=c++11",
"file": "$(path)/a1/compilations.cpp"
},
{
"directory": "$(path)/a2/",
"command": "clang++ -o compilations.o -c $(path)/a2/compilations.cpp -std=c++11",
"file": "$(path)/a2/compilations.cpp"
},
{
"directory": "$(path)/a3/",
"command": "clang++ -o compilations.o -c $(path)/a3/compilations.cpp -std=c++11",
"file": "$(path)/a3/compilations.cpp"
}
]

View File

@ -1,4 +0,0 @@
void foo() {
int *p = 0;
int *k = nullptr;
}

View File

@ -1,4 +0,0 @@
void foo() {
int *p = nullptr;
int *k = nullptr;
}

View File

@ -1,24 +0,0 @@
// The following block tests:
// - A compilation database is detected from build path specified by -p and
// -include was provided.
// Create directory structure
// a1, a2 and a3 are specified paths for files in the compilation database.
// RUN: rm -rf %T/CompilationInc
// RUN: mkdir -p %T/CompilationInc
// RUN: mkdir -p %T/CompilationInc/a1
// RUN: mkdir -p %T/CompilationInc/a2
// RUN: mkdir -p %T/CompilationInc/a3
// This test uses a compilation database
// RUN: sed -e 's#$(path)#%/T/CompilationInc#g' %S/Inputs/compile_commands.json > %T/CompilationInc/compile_commands.json
// Check that files are transformed when -p and -include are specified.
// RUN: cp %S/Inputs/compilations.cpp %T/CompilationInc/a1
// RUN: cp %S/Inputs/compilations.cpp %T/CompilationInc/a2
// RUN: cp %S/Inputs/compilations.cpp %T/CompilationInc/a3
// RUN: clang-modernize -use-nullptr -p=%T/CompilationInc -include=%T/CompilationInc/a1,%T/CompilationInc/a3
// RUN: diff -b %S/Inputs/compilations_expected.cpp %T/CompilationInc/a1/compilations.cpp
// RUN: not diff -b %S/Inputs/compilations_expected.cpp %T/CompilationInc/a2/compilations.cpp
// RUN: diff -b %S/Inputs/compilations_expected.cpp %T/CompilationInc/a3/compilations.cpp

View File

@ -1,22 +0,0 @@
// Test that only specified sources are transformed when -p and -include are
// specified along with sources.
// Create directory structure
// a1, a2 and a3 are specified paths for files in the compilation database.
// RUN: rm -rf %T/CompilationIncSources
// RUN: mkdir -p %T/CompilationIncSources
// RUN: mkdir -p %T/CompilationIncSources/a1
// RUN: mkdir -p %T/CompilationIncSources/a2
// RUN: mkdir -p %T/CompilationIncSources/a3
// This test uses a compilation database
// RUN: sed -e 's#$(path)#%/T/CompilationIncSources#g' %S/Inputs/compile_commands.json > %T/CompilationIncSources/compile_commands.json
// RUN: cp %S/Inputs/compilations.cpp %T/CompilationIncSources/a1
// RUN: cp %S/Inputs/compilations.cpp %T/CompilationIncSources/a2
// RUN: cp %S/Inputs/compilations.cpp %T/CompilationIncSources/a3
// RUN: clang-modernize -use-nullptr -p=%T/CompilationIncSources -include=%T/CompilationIncSources %T/CompilationIncSources/a2/compilations.cpp
// RUN: not diff -b %S/Inputs/compilations_expected.cpp %T/CompilationIncSources/a1/compilations.cpp
// RUN: diff -b %S/Inputs/compilations_expected.cpp %T/CompilationIncSources/a2/compilations.cpp
// RUN: not diff -b %S/Inputs/compilations_expected.cpp %T/CompilationIncSources/a3/compilations.cpp

View File

@ -1,24 +0,0 @@
// The following block tests:
// - A compilation database is detected from build path specified by -p but
// neither sources nor -include was provided.
// Create directory structure
// a1, a2 and a3 are specified paths for files in the compilation database.
// RUN: rm -rf %T/CompilationNotInc
// RUN: mkdir -p %T/CompilationNotInc
// RUN: mkdir -p %T/CompilationNotInc/a1
// RUN: mkdir -p %T/CompilationNotInc/a2
// RUN: mkdir -p %T/CompilationNotInc/a3
// This test uses a compilation database
// RUN: sed -e 's#$(path)#%/T/CompilationNotInc#g' %S/Inputs/compile_commands.json > %T/CompilationNotInc/compile_commands.json
// Check that no files are transformed when -p is specified but not -include.
// RUN: cp %S/Inputs/compilations.cpp %T/CompilationNotInc/a1
// RUN: cp %S/Inputs/compilations.cpp %T/CompilationNotInc/a2
// RUN: cp %S/Inputs/compilations.cpp %T/CompilationNotInc/a3
// RUN: not clang-modernize -use-nullptr -p=%T/CompilationNotInc
// RUN: not diff -b %T/compilations_expected.cpp %T/CompilationNotInc/a1/compilations.cpp
// RUN: not diff -b %T/compilations_expected.cpp %T/CompilationNotInc/a2/compilations.cpp
// RUN: not diff -b %T/compilations_expected.cpp %T/CompilationNotInc/a3/compilations.cpp

View File

@ -1,24 +0,0 @@
// The following block tests:
// - A compilation database is detected from build path specified by -p and
// files are provided.
// Create directory structure
// a1, a2 and a3 are specified paths for files in the compilation database.
// RUN: rm -rf %T/DetectFromPath
// RUN: mkdir -p %T/DetectFromPath
// RUN: mkdir -p %T/DetectFromPath/a1
// RUN: mkdir -p %T/DetectFromPath/a2
// RUN: mkdir -p %T/DetectFromPath/a3
// This test uses a compilation database
// RUN: sed -e 's#$(path)#%/T/DetectFromPath#g' %S/Inputs/compile_commands.json > %T/DetectFromPath/compile_commands.json
// Check that files are transformed when -p is provided and files are specified.
// RUN: cp %S/Inputs/compilations.cpp %T/DetectFromPath/a1
// RUN: cp %S/Inputs/compilations.cpp %T/DetectFromPath/a2
// RUN: cp %S/Inputs/compilations.cpp %T/DetectFromPath/a3
// RUN: clang-modernize -use-nullptr -p=%T/DetectFromPath %T/DetectFromPath/a1/compilations.cpp %T/DetectFromPath/a3/compilations.cpp
// RUN: diff -b %S/Inputs/compilations_expected.cpp %T/DetectFromPath/a1/compilations.cpp
// RUN: not diff -b %S/Inputs/compilations_expected.cpp %T/DetectFromPath/a2/compilations.cpp
// RUN: diff -b %S/Inputs/compilations_expected.cpp %T/DetectFromPath/a3/compilations.cpp

View File

@ -1,23 +0,0 @@
// The following block tests:
// - A compilation database is detected from source0.
// Create directory structure
// a1, a2 and a3 are specified paths for files in the compilation database.
// RUN: rm -rf %T/DetectFromSource
// RUN: mkdir -p %T/DetectFromSource
// RUN: mkdir -p %T/DetectFromSource/a1
// RUN: mkdir -p %T/DetectFromSource/a2
// RUN: mkdir -p %T/DetectFromSource/a3
// This test uses a compilation database
// RUN: sed -e 's#$(path)#%/T/DetectFromSource#g' %S/Inputs/compile_commands.json > %T/DetectFromSource/compile_commands.json
// Check that a compilation database can be auto-detected from source0
// RUN: cp %S/Inputs/compilations.cpp %T/DetectFromSource/a1
// RUN: cp %S/Inputs/compilations.cpp %T/DetectFromSource/a2
// RUN: cp %S/Inputs/compilations.cpp %T/DetectFromSource/a3
// RUN: clang-modernize -use-nullptr %T/DetectFromSource/a1/compilations.cpp %T/DetectFromSource/a3/compilations.cpp
// RUN: diff -b %S/Inputs/compilations_expected.cpp %T/DetectFromSource/a1/compilations.cpp
// RUN: not diff -b %S/Inputs/compilations_expected.cpp %T/DetectFromSource/a2/compilations.cpp
// RUN: diff -b %S/Inputs/compilations_expected.cpp %T/DetectFromSource/a3/compilations.cpp

View File

@ -1,18 +0,0 @@
// The following block tests that files are transformed when -- is specified.
// Create directory structure
// a1, a2 and a3 are specified paths for files in the compilation database.
// RUN: rm -rf %T/FixedComp
// RUN: mkdir -p %T/FixedComp
// RUN: mkdir -p %T/FixedComp/a1
// RUN: mkdir -p %T/FixedComp/a2
// RUN: mkdir -p %T/FixedComp/a3
// RUN: cp %S/Inputs/compilations.cpp %T/FixedComp/a1
// RUN: cp %S/Inputs/compilations.cpp %T/FixedComp/a2
// RUN: cp %S/Inputs/compilations.cpp %T/FixedComp/a3
// RUN: clang-modernize -use-nullptr %T/FixedComp/a1/compilations.cpp %T/FixedComp/a3/compilations.cpp --
// RUN: diff -b %S/Inputs/compilations_expected.cpp %T/FixedComp/a1/compilations.cpp
// RUN: not diff -b %S/Inputs/compilations_expected.cpp %T/FixedComp/a2/compilations.cpp
// RUN: diff -b %S/Inputs/compilations_expected.cpp %T/FixedComp/a3/compilations.cpp

View File

@ -1,20 +0,0 @@
// The following block tests:
// - A fixed compilation database is provided and -exclude was also used.
// Create directory structure
// a1, a2 and a3 are specified paths for files in the compilation database.
// RUN: rm -rf %T/FixedCompInc
// RUN: mkdir -p %T/FixedCompInc
// RUN: mkdir -p %T/FixedCompInc/a1
// RUN: mkdir -p %T/FixedCompInc/a2
// RUN: mkdir -p %T/FixedCompInc/a3
// Check that only files not explicitly excluded are transformed.
// RUN: cp %S/Inputs/compilations.cpp %T/FixedCompInc/a1
// RUN: cp %S/Inputs/compilations.cpp %T/FixedCompInc/a2
// RUN: cp %S/Inputs/compilations.cpp %T/FixedCompInc/a3
// RUN: clang-modernize -use-nullptr %T/FixedCompInc/a1/compilations.cpp %T/FixedCompInc/a2/compilations.cpp %T/FixedCompInc/a3/compilations.cpp -exclude=%T/FixedCompInc/a2 --
// RUN: diff -b %S/Inputs/compilations_expected.cpp %T/FixedCompInc/a1/compilations.cpp
// RUN: not diff -b %S/Inputs/compilations_expected.cpp %T/FixedCompInc/a2/compilations.cpp
// RUN: diff -b %S/Inputs/compilations_expected.cpp %T/FixedCompInc/a3/compilations.cpp

View File

@ -1,22 +0,0 @@
// The following block tests:
// - Neither -p nor -- was specified and a compilation database is detected
// from source0 but the file isn't found the compilation database then
// it's transformed using a fixed compilation database with c++11 support.
// (-- -std=c++11).
// Create directory structure
// a1, a2 and a3 are specified paths for files in the compilation database but
// not a4.
// RUN: rm -rf %T/NoCompilation
// RUN: mkdir -p %T/NoCompilation
// RUN: mkdir -p %T/NoCompilation/a1
// RUN: mkdir -p %T/NoCompilation/a2
// RUN: mkdir -p %T/NoCompilation/a3
// RUN: mkdir -p %T/NoCompilation/a4
// This test uses of a compilation database
// RUN: sed -e 's#$(path)#%/T/NoCompilation#g' %S/Inputs/compile_commands.json > %T/NoCompilation/compile_commands.json
// RUN: cp %S/Inputs/cpp11.cpp %T/NoCompilation/a4
// RUN: clang-modernize -use-nullptr %T/NoCompilation/a4/cpp11.cpp
// RUN: diff -b %S/Inputs/cpp11_expected.cpp %T/NoCompilation/a4/cpp11.cpp

View File

@ -1,42 +0,0 @@
---
# BasedOnStyle: Google
AccessModifierOffset: -1
ConstructorInitializerIndentWidth: 4
AlignEscapedNewlinesLeft: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: true
AlwaysBreakBeforeMultilineStrings: true
BreakBeforeBinaryOperators: false
BreakConstructorInitializersBeforeComma: false
BinPackParameters: true
ColumnLimit: 80
ConstructorInitializerAllOnOneLineOrOnePerLine: true
DerivePointerBinding: true
ExperimentalAutoDetectBinPacking: false
IndentCaseLabels: true
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCSpaceBeforeProtocolList: false
PenaltyBreakComment: 60
PenaltyBreakString: 1000
PenaltyBreakFirstLessLess: 120
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerBindsToType: true
SpacesBeforeTrailingComments: 2
Cpp11BracedListStyle: true
Standard: Auto
IndentWidth: 2
TabWidth: 8
UseTab: Never
BreakBeforeBraces: Attach
SpacesInParentheses: false
SpaceInEmptyParentheses: false
SpacesInCStyleCastParentheses: false
SpaceAfterControlStatementKeyword: true
SpaceBeforeAssignmentOperators: true
...

View File

@ -1,44 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: clang-modernize -format -use-auto %t.cpp
// RUN: FileCheck --strict-whitespace -input-file=%t.cpp %s
// Ensure that -style is forwarded to clang-apply-replacements by using a style
// other than LLVM and ensuring the result is styled as requested.
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: clang-modernize -format -style=Google -use-nullptr %t.cpp
// RUN: FileCheck --check-prefix=Google --strict-whitespace -input-file=%t.cpp %s
// Ensure -style-config is forwarded to clang-apply-replacements. The .clang-format
// in %S/Inputs is a dump of the Google style so the same test can be used.
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: clang-modernize -format -style=file -style-config=%S/Inputs -use-nullptr %t.cpp
// RUN: FileCheck --check-prefix=Google --strict-whitespace -input-file=%t.cpp %s
class MyType012345678901234567890123456789 {
public:
MyType012345678901234567890123456789()
: iiiiiiiiiiii(0), jjjjjjjjjjjj(0), kkkkkkkkkkkk(0), mmmmmmmmmmmm(0),
nnnnnnnnnnnn(0) {}
// Google: iiiiiiiiiiii(nullptr),
// Google-NEXT: jjjjjjjjjjjj(nullptr),
// Google-NEXT: kkkkkkkkkkkk(nullptr),
// Google-NEXT: mmmmmmmmmmmm(nullptr),
// Google-NEXT: nnnnnnnnnnnn(nullptr) {}
private:
int *iiiiiiiiiiii;
int *jjjjjjjjjjjj;
int *kkkkkkkkkkkk;
int *mmmmmmmmmmmm;
int *nnnnnnnnnnnn;
};
int f() {
MyType012345678901234567890123456789 *a =
new MyType012345678901234567890123456789();
// CHECK: {{^\ \ auto\ a\ \=\ new\ MyType012345678901234567890123456789\(\);}}
delete a;
return 0;
}

View File

@ -1,8 +0,0 @@
void update(int (&arr)[10]) {
int val = 1;
for (unsigned i = 0; i < sizeof(arr)/sizeof(int); ++i) {
arr[i] = val++;
// CHECK: for (auto & elem : arr) {
// CHECK-NEXT: elem = val++;
}
}

View File

@ -1,16 +0,0 @@
// This is just a dummy run command to keep lit happy. Tests for this file are
// in main.cpp
// RUN: true
#include "common.h"
void func1(int &I) {
}
void func2() {
container C1;
container C2;
for (container::iterator I = C1.begin(), E = C1.end(); I != E; ++I) {
C2.push_back(*I);
}
}

View File

@ -1,27 +0,0 @@
#ifndef CPP11_MIGRATE_TEST_HEADER_REPLACEMENTS_COMMON_H
#define CPP11_MIGRATE_TEST_HEADER_REPLACEMENTS_COMMON_H
struct container {
struct iterator {
int &operator*();
const int &operator*() const;
iterator &operator++();
bool operator!=(const iterator &other);
};
iterator begin();
iterator end();
void push_back(const int &);
};
void func1(int &I);
void func2();
void dostuff() {
container C;
for (container::iterator I = C.begin(), E = C.end(); I != E; ++I) {
func1(*I);
}
}
#endif // CPP11_MIGRATE_TEST_HEADER_REPLACEMENTS_COMMON_H

View File

@ -1,20 +0,0 @@
---
MainSourceFile: '$(path)/common.cpp'
Replacements:
- FilePath: '$(path)/common.h'
Offset: 506
Length: 2
ReplacementText: elem
- FilePath: '$(path)/common.h'
Offset: 432
Length: 61
ReplacementText: '(auto & elem : C)'
- FilePath: '$(path)/common.cpp'
Offset: 289
Length: 2
ReplacementText: elem
- FilePath: '$(path)/common.cpp'
Offset: 206
Length: 63
ReplacementText: '(auto & elem : C1)'
...

View File

@ -1,36 +0,0 @@
// The following block tests the following:
// - Only 1 file is generated per translation unit
// - Replacements are written in YAML that matches the expected YAML file
// The test is run in %T/SerializeTest so it's easy to create a clean test
// directory.
//
// RUN: rm -rf %T/SerializeTest
// RUN: mkdir -p %T/SerializeTest
// RUN: cp %S/main.cpp %S/common.cpp %S/common.h %T/SerializeTest
// RUN: clang-modernize -loop-convert -serialize-replacements -serialize-dir=%T/SerializeTest -include=%T/SerializeTest %T/SerializeTest/main.cpp %T/SerializeTest/common.cpp --
// Check that only 1 file is generated per translation unit
// RUN: ls -1 %T/SerializeTest | FileCheck %s --check-prefix=MAIN_CPP
// RUN: ls -1 %T/SerializeTest | FileCheck %s --check-prefix=COMMON_CPP
// We need to put the build path to the expected YAML file to diff against the generated one.
// RUN: sed -e 's#$(path)#%/T/SerializeTest#g' -e "s#[^[:space:]]'[^[:space:]]#''#g" -e "s#'\([-a-zA-Z0-9_/^., \t]*\)'#\1#g" %S/main_expected.yaml > %T/SerializeTest/main_expected.yaml
// RUN: sed -i -e 's#\\#/#g' %T/SerializeTest/main.cpp_*.yaml
// RUN: diff -b %T/SerializeTest/main_expected.yaml %T/SerializeTest/main.cpp_*.yaml
// RUN: sed -e 's#$(path)#%/T/SerializeTest#g' -e "s#[^[:space:]]'[^[:space:]]#''#g" -e "s#'\([-a-zA-Z0-9_/^., \t]*\)'#\1#g" %S/common_expected.yaml > %T/SerializeTest/common_expected.yaml
// RUN: sed -i -e 's#\\#/#g' %T/SerializeTest/common.cpp_*.yaml
// RUN: diff -b %T/SerializeTest/common_expected.yaml %T/SerializeTest/common.cpp_*.yaml
//
// The following are for FileCheck when used on output of 'ls'. See above.
// MAIN_CPP: {{^main.cpp_.*.yaml$}}
// MAIN_CPP-NOT: {{main.cpp_.*.yaml}}
//
// COMMON_CPP: {{^common.cpp_.*.yaml$}}
// COMMON_CPP-NOT: {{common.cpp_.*.yaml}}
// REQUIRES: shell
#include "common.h"
void test_header_replacement() {
dostuff();
func2();
}

View File

@ -1,12 +0,0 @@
---
MainSourceFile: '$(path)/main.cpp'
Replacements:
- FilePath: '$(path)/common.h'
Offset: 506
Length: 2
ReplacementText: elem
- FilePath: '$(path)/common.h'
Offset: 432
Length: 61
ReplacementText: '(auto & elem : C)'
...

Some files were not shown because too many files have changed in this diff Show More