forked from OSchip/llvm-project
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:
parent
0e59c516c4
commit
23f04fd469
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -1,7 +0,0 @@
|
|||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${ClangReplaceLocation}
|
||||
)
|
||||
|
||||
add_subdirectory(tool)
|
||||
add_subdirectory(Core)
|
|
@ -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
|
||||
)
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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
|
|
@ -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;
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
|
@ -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
|
|
@ -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 ¶m' -> '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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
||||
};
|
|
@ -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) > $@
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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`.
|
|
@ -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`.
|
|
@ -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 User’s 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 User’s 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/
|
|
@ -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
|
|
@ -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.
|
|
@ -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.
|
|
@ -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).
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -15,7 +15,6 @@ Contents
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
clang-modernize
|
||||
clang-tidy/index
|
||||
modularize
|
||||
pp-trace
|
||||
|
|
|
@ -36,7 +36,6 @@ set(CLANG_TOOLS_TEST_DEPS
|
|||
|
||||
# Individual tools we test.
|
||||
clang-apply-replacements
|
||||
clang-modernize
|
||||
clang-rename
|
||||
clang-query
|
||||
clang-tidy
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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(); }
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
};
|
|
@ -1,3 +0,0 @@
|
|||
void foo() {
|
||||
int *p = 0;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
void foo() {
|
||||
int *p = nullptr;
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
|
@ -1,4 +0,0 @@
|
|||
void foo() {
|
||||
int *p = 0;
|
||||
int *k = nullptr;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
void foo() {
|
||||
int *p = nullptr;
|
||||
int *k = nullptr;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
...
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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++;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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)'
|
||||
...
|
|
@ -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();
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue