forked from OSchip/llvm-project
cpp11-migrate: Add Pass-By-Value Transform
Currently only constructor parameters stored in class-local storage are modified to make use of the pass-by-value idiom but this is a base that can be be further improved to handle more situations. llvm-svn: 189363
This commit is contained in:
parent
fe48aaf1a4
commit
dcdae946f3
|
@ -139,6 +139,12 @@ public:
|
||||||
bool isFileModifiable(const clang::SourceManager &SM,
|
bool isFileModifiable(const clang::SourceManager &SM,
|
||||||
const clang::SourceLocation &Loc) const;
|
const clang::SourceLocation &Loc) const;
|
||||||
|
|
||||||
|
/// \brief Whether a transformation with a risk level of \p RiskLevel is
|
||||||
|
/// acceptable or not.
|
||||||
|
bool isAcceptableRiskLevel(RiskLevel RiskLevel) const {
|
||||||
|
return RiskLevel <= GlobalOptions.MaxRiskLevel;
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Called before parsing a translation unit for a FrontendAction.
|
/// \brief Called before parsing a translation unit for a FrontendAction.
|
||||||
///
|
///
|
||||||
/// Transform uses this function to apply file overrides and start
|
/// Transform uses this function to apply file overrides and start
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
//===-- 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(
|
||||||
|
FileOverrides &InputStates, const tooling::CompilationDatabase &Database,
|
||||||
|
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE {
|
||||||
|
ClangTool Tool(Database, SourcePaths);
|
||||||
|
unsigned AcceptedChanges = 0;
|
||||||
|
unsigned RejectedChanges = 0;
|
||||||
|
MatchFinder Finder;
|
||||||
|
ConstructorParamReplacer Replacer(getReplacements(), AcceptedChanges,
|
||||||
|
RejectedChanges,
|
||||||
|
/*Owner=*/ *this);
|
||||||
|
|
||||||
|
Finder.addMatcher(makePassByValueCtorParamMatcher(), &Replacer);
|
||||||
|
|
||||||
|
// make the replacer available to handleBeginSource()
|
||||||
|
this->Replacer = &Replacer;
|
||||||
|
|
||||||
|
setOverrides(InputStates);
|
||||||
|
|
||||||
|
if (Tool.run(createActionFactory(Finder))) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) LLVM_OVERRIDE {
|
||||||
|
return new PassByValueTransform(Opts);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
|
@ -0,0 +1,74 @@
|
||||||
|
//===-- 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 CPP11_MIGRATE_PASS_BY_VALUE_H
|
||||||
|
#define CPP11_MIGRATE_PASS_BY_VALUE_H
|
||||||
|
|
||||||
|
#include "Core/Transform.h"
|
||||||
|
#include "Core/IncludeDirectives.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(0) {}
|
||||||
|
|
||||||
|
/// \see Transform::apply().
|
||||||
|
virtual int apply(FileOverrides &InputStates,
|
||||||
|
const clang::tooling::CompilationDatabase &Database,
|
||||||
|
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// \brief Setups the \c IncludeDirectives for the replacer.
|
||||||
|
virtual bool handleBeginSource(clang::CompilerInstance &CI,
|
||||||
|
llvm::StringRef Filename) LLVM_OVERRIDE;
|
||||||
|
|
||||||
|
llvm::OwningPtr<IncludeDirectives> IncludeManager;
|
||||||
|
ConstructorParamReplacer *Replacer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CPP11_MIGRATE_PASS_BY_VALUE_H
|
|
@ -0,0 +1,167 @@
|
||||||
|
//===-- 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 "PassByValueMatchers.h"
|
||||||
|
#include "Core/IncludeDirectives.h"
|
||||||
|
#include "Core/Transform.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 accross 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 unecessary 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(AllParamDecls.size());
|
||||||
|
for (unsigned I = 0, E = AllParamDecls.size(); I != E; ++I) {
|
||||||
|
TypeLoc ParamTL = AllParamDecls[I]->getTypeSourceInfo()->getTypeLoc();
|
||||||
|
SourceRange Range(AllParamDecls[I]->getLocStart(), ParamTL.getLocEnd());
|
||||||
|
CharSourceRange CharRange = Lexer::makeFileCharRange(
|
||||||
|
CharSourceRange::getTokenRange(Range), SM, LangOptions());
|
||||||
|
TypeLoc ValueTypeLoc = ParamTL;
|
||||||
|
// transform non-value parameters (e.g: const-ref) to values
|
||||||
|
if (!ParamTL.getNextTypeLoc().isNull())
|
||||||
|
ValueTypeLoc = ParamTL.getNextTypeLoc();
|
||||||
|
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[I] = 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()) {
|
||||||
|
Replaces.insert(IncludeReplace);
|
||||||
|
AcceptedChanges++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const-ref params becomes values (const Foo & -> Foo)
|
||||||
|
Replaces.insert(ParamReplaces.begin(), ParamReplaces.end());
|
||||||
|
AcceptedChanges += ParamReplaces.size();
|
||||||
|
|
||||||
|
// move the value in the init-list
|
||||||
|
Replaces.insert(Replacement(
|
||||||
|
SM, Initializer->getLParenLoc().getLocWithOffset(1), 0, "std::move("));
|
||||||
|
Replaces.insert(Replacement(SM, Initializer->getRParenLoc(), 0, ")"));
|
||||||
|
AcceptedChanges += 2;
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
//===-- 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 CPP11_MIGRATE_PASS_BY_VALUE_ACTIONS_H
|
||||||
|
#define CPP11_MIGRATE_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(clang::tooling::Replacements &Replaces,
|
||||||
|
unsigned &AcceptedChanges, unsigned &RejectedChanges,
|
||||||
|
const Transform &Owner)
|
||||||
|
: Replaces(Replaces), AcceptedChanges(AcceptedChanges),
|
||||||
|
RejectedChanges(RejectedChanges), Owner(Owner), IncludeManager(0) {}
|
||||||
|
|
||||||
|
void setIncludeDirectives(IncludeDirectives *Includes) {
|
||||||
|
IncludeManager = Includes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// \brief Entry point to the callback called when matches are made.
|
||||||
|
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result)
|
||||||
|
LLVM_OVERRIDE;
|
||||||
|
|
||||||
|
clang::tooling::Replacements &Replaces;
|
||||||
|
unsigned &AcceptedChanges;
|
||||||
|
unsigned &RejectedChanges;
|
||||||
|
const Transform &Owner;
|
||||||
|
IncludeDirectives *IncludeManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CPP11_MIGRATE_PASS_BY_VALUE_ACTIONS_H
|
|
@ -0,0 +1,80 @@
|
||||||
|
//===-- 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Matches non-deleted copy constructors.
|
||||||
|
///
|
||||||
|
/// Given
|
||||||
|
/// \code
|
||||||
|
/// struct Foo { Foo(const Foo &) = default; };
|
||||||
|
/// struct Bar { Bar(const Bar &) = deleted; };
|
||||||
|
/// \endcode
|
||||||
|
/// constructorDecl(isNonDeletedCopyConstructor())
|
||||||
|
/// matches "Foo(const Foo &)".
|
||||||
|
AST_MATCHER(CXXConstructorDecl, isNonDeletedCopyConstructor) {
|
||||||
|
return Node.isCopyConstructor() && !Node.isDeleted();
|
||||||
|
}
|
||||||
|
} // namespace ast_matchers
|
||||||
|
} // namespace clang
|
||||||
|
|
||||||
|
using namespace clang;
|
||||||
|
using namespace clang::ast_matchers;
|
||||||
|
|
||||||
|
DeclarationMatcher makePassByValueCtorParamMatcher() {
|
||||||
|
return constructorDecl(
|
||||||
|
forEachConstructorInitializer(ctorInitializer(
|
||||||
|
// 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(constructExpr(
|
||||||
|
has(declRefExpr(to(parmVarDecl().bind(PassByValueParamId)))),
|
||||||
|
hasDeclaration(constructorDecl(
|
||||||
|
isNonDeletedCopyConstructor(),
|
||||||
|
hasDeclContext(recordDecl(isMoveConstructible())))))))
|
||||||
|
.bind(PassByValueInitializerId)))
|
||||||
|
.bind(PassByValueCtorId);
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
//===-- 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 CPP11_MIGRATE_REPLACE_AUTO_PTR_MATCHERS_H
|
||||||
|
#define CPP11_MIGRATE_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 // CPP11_MIGRATE_REPLACE_AUTO_PTR_MATCHERS_H
|
|
@ -19,6 +19,9 @@ list(APPEND Cpp11MigrateSources ${UseAutoSources})
|
||||||
file(GLOB_RECURSE AddOverrideSources "../AddOverride/*.cpp")
|
file(GLOB_RECURSE AddOverrideSources "../AddOverride/*.cpp")
|
||||||
list(APPEND Cpp11MigrateSources ${AddOverrideSources})
|
list(APPEND Cpp11MigrateSources ${AddOverrideSources})
|
||||||
|
|
||||||
|
file(GLOB_RECURSE PassByValueSources "../PassByValue/*.cpp")
|
||||||
|
list(APPEND Cpp11MigrateSources ${PassByValueSources})
|
||||||
|
|
||||||
file(GLOB_RECURSE ReplaceAutoPtrSources "../ReplaceAutoPtr/*.cpp")
|
file(GLOB_RECURSE ReplaceAutoPtrSources "../ReplaceAutoPtr/*.cpp")
|
||||||
list(APPEND Cpp11MigrateSources ${ReplaceAutoPtrSources})
|
list(APPEND Cpp11MigrateSources ${ReplaceAutoPtrSources})
|
||||||
|
|
||||||
|
|
|
@ -414,6 +414,7 @@ int main(int argc, const char **argv) {
|
||||||
// These anchors are used to force the linker to link the transforms
|
// These anchors are used to force the linker to link the transforms
|
||||||
extern volatile int AddOverrideTransformAnchorSource;
|
extern volatile int AddOverrideTransformAnchorSource;
|
||||||
extern volatile int LoopConvertTransformAnchorSource;
|
extern volatile int LoopConvertTransformAnchorSource;
|
||||||
|
extern volatile int PassByValueTransformAnchorSource;
|
||||||
extern volatile int ReplaceAutoPtrTransformAnchorSource;
|
extern volatile int ReplaceAutoPtrTransformAnchorSource;
|
||||||
extern volatile int UseAutoTransformAnchorSource;
|
extern volatile int UseAutoTransformAnchorSource;
|
||||||
extern volatile int UseNullptrTransformAnchorSource;
|
extern volatile int UseNullptrTransformAnchorSource;
|
||||||
|
@ -421,6 +422,7 @@ extern volatile int UseNullptrTransformAnchorSource;
|
||||||
static int TransformsAnchorsDestination[] = {
|
static int TransformsAnchorsDestination[] = {
|
||||||
AddOverrideTransformAnchorSource,
|
AddOverrideTransformAnchorSource,
|
||||||
LoopConvertTransformAnchorSource,
|
LoopConvertTransformAnchorSource,
|
||||||
|
PassByValueTransformAnchorSource,
|
||||||
ReplaceAutoPtrTransformAnchorSource,
|
ReplaceAutoPtrTransformAnchorSource,
|
||||||
UseAutoTransformAnchorSource,
|
UseAutoTransformAnchorSource,
|
||||||
UseNullptrTransformAnchorSource
|
UseNullptrTransformAnchorSource
|
||||||
|
|
|
@ -30,6 +30,8 @@ SOURCES += $(addprefix ../UseAuto/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../UseAut
|
||||||
BUILT_SOURCES += $(ObjDir)/../UseAuto/.objdir
|
BUILT_SOURCES += $(ObjDir)/../UseAuto/.objdir
|
||||||
SOURCES += $(addprefix ../AddOverride/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../AddOverride/*.cpp)))
|
SOURCES += $(addprefix ../AddOverride/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../AddOverride/*.cpp)))
|
||||||
BUILT_SOURCES += $(ObjDir)/../AddOverride/.objdir
|
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)))
|
SOURCES += $(addprefix ../ReplaceAutoPtr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/../ReplaceAutoPtr/*.cpp)))
|
||||||
BUILT_SOURCES += $(ObjDir)/../ReplaceAutoPtr/.objdir
|
BUILT_SOURCES += $(ObjDir)/../ReplaceAutoPtr/.objdir
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,7 @@ General Command Line Options
|
||||||
=============== ===== === ==== ====
|
=============== ===== === ==== ====
|
||||||
AddOverride (1) 3.0 4.7 14 8
|
AddOverride (1) 3.0 4.7 14 8
|
||||||
LoopConvert 3.0 4.6 13 11
|
LoopConvert 3.0 4.6 13 11
|
||||||
|
PassByValue 3.0 4.6 13 11
|
||||||
ReplaceAutoPtr 3.0 4.6 13 11
|
ReplaceAutoPtr 3.0 4.6 13 11
|
||||||
UseAuto 2.9 4.4 12 10
|
UseAuto 2.9 4.4 12 10
|
||||||
UseNullptr 3.0 4.6 12.1 10
|
UseNullptr 3.0 4.6 12.1 10
|
||||||
|
@ -226,6 +227,12 @@ Transform-Specific Command Line Options
|
||||||
projects that use such macros to maintain build compatibility with non-C++11
|
projects that use such macros to maintain build compatibility with non-C++11
|
||||||
code.
|
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
|
.. option:: -replace-auto_ptr
|
||||||
|
|
||||||
Replace ``std::auto_ptr`` (deprecated in C++11) by ``std::unique_ptr`` and
|
Replace ``std::auto_ptr`` (deprecated in C++11) by ``std::unique_ptr`` and
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
.. 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::
|
||||||
|
|
||||||
|
$ cpp11-migrate -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"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
For more information about this idiom, read: `Want Speed? Pass by Value`_.
|
||||||
|
|
||||||
|
.. _Want Speed? Pass by Value: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
|
||||||
|
|
|
@ -11,6 +11,7 @@ C++11 Migrator User's Manual
|
||||||
UseNullptrTransform
|
UseNullptrTransform
|
||||||
LoopConvertTransform
|
LoopConvertTransform
|
||||||
AddOverrideTransform
|
AddOverrideTransform
|
||||||
|
PassByValueTransform
|
||||||
ReplaceAutoPtrTransform
|
ReplaceAutoPtrTransform
|
||||||
MigratorUsage
|
MigratorUsage
|
||||||
|
|
||||||
|
@ -116,4 +117,6 @@ independently enabled. The transforms currently implemented are:
|
||||||
|
|
||||||
* :doc:`AddOverrideTransform`
|
* :doc:`AddOverrideTransform`
|
||||||
|
|
||||||
|
* :doc:`PassByValueTransform`
|
||||||
|
|
||||||
* :doc:`ReplaceAutoPtrTransform`
|
* :doc:`ReplaceAutoPtrTransform`
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||||
|
// RUN: cpp11-migrate -pass-by-value %t.cpp -- -std=c++11 -I %S
|
||||||
|
// RUN: FileCheck -input-file=%t.cpp %s
|
||||||
|
//
|
||||||
|
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
|
||||||
|
// RUN: cpp11-migrate -pass-by-value %t.cpp -- -std=c++11 -I %S
|
||||||
|
// RUN: FileCheck -check-prefix=SAFE_RISK -input-file=%t.cpp %s
|
||||||
|
|
||||||
|
#include "basic.h"
|
||||||
|
// CHECK: #include <utility>
|
||||||
|
|
||||||
|
// Test that when the class declaration can't be modified we won't modify the
|
||||||
|
// definition either.
|
||||||
|
UnmodifiableClass::UnmodifiableClass(const Movable &M) : M(M) {}
|
||||||
|
// CHECK: UnmodifiableClass::UnmodifiableClass(const Movable &M) : M(M) {}
|
||||||
|
|
||||||
|
struct A {
|
||||||
|
A(const Movable &M) : M(M) {}
|
||||||
|
// CHECK: A(Movable M) : M(std::move(M)) {}
|
||||||
|
// SAFE_RISK: A(const Movable &M) : M(M) {}
|
||||||
|
Movable M;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that we aren't modifying other things than a parameter
|
||||||
|
Movable GlobalObj;
|
||||||
|
struct B {
|
||||||
|
B(const Movable &M) : M(GlobalObj) {}
|
||||||
|
// CHECK: B(const Movable &M) : M(GlobalObj) {}
|
||||||
|
Movable M;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that a parameter with more than one reference to it won't be changed.
|
||||||
|
struct C {
|
||||||
|
// Tests extra-reference in body
|
||||||
|
C(const Movable &M) : M(M) { this->i = M.a; }
|
||||||
|
// CHECK: C(const Movable &M) : M(M) { this->i = M.a; }
|
||||||
|
|
||||||
|
// Tests extra-reference in init-list
|
||||||
|
C(const Movable &M, int) : M(M), i(M.a) {}
|
||||||
|
// CHECK: C(const Movable &M, int) : M(M), i(M.a) {}
|
||||||
|
Movable M;
|
||||||
|
int i;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that both declaration and definition are updated
|
||||||
|
struct D {
|
||||||
|
D(const Movable &M);
|
||||||
|
// CHECK: D(Movable M);
|
||||||
|
Movable M;
|
||||||
|
};
|
||||||
|
D::D(const Movable &M) : M(M) {}
|
||||||
|
// CHECK: D::D(Movable M) : M(std::move(M)) {}
|
||||||
|
|
||||||
|
// Test with default parameter
|
||||||
|
struct E {
|
||||||
|
E(const Movable &M = Movable()) : M(M) {}
|
||||||
|
// CHECK: E(Movable M = Movable()) : M(std::move(M)) {}
|
||||||
|
Movable M;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test with object that can't be moved
|
||||||
|
struct F {
|
||||||
|
F(const NotMovable &NM) : NM(NM) {}
|
||||||
|
// CHECK: F(const NotMovable &NM) : NM(NM) {}
|
||||||
|
NotMovable NM;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test unnamed parameter in declaration
|
||||||
|
struct G {
|
||||||
|
G(const Movable &);
|
||||||
|
// CHECK: G(Movable );
|
||||||
|
Movable M;
|
||||||
|
};
|
||||||
|
G::G(const Movable &M) : M(M) {}
|
||||||
|
// CHECK: G::G(Movable M) : M(std::move(M)) {}
|
||||||
|
|
||||||
|
// Test parameter with and without qualifier
|
||||||
|
namespace ns_H {
|
||||||
|
typedef ::Movable HMovable;
|
||||||
|
}
|
||||||
|
struct H {
|
||||||
|
H(const ns_H::HMovable &M);
|
||||||
|
// CHECK: H(ns_H::HMovable M);
|
||||||
|
ns_H::HMovable M;
|
||||||
|
};
|
||||||
|
using namespace ns_H;
|
||||||
|
H::H(const HMovable &M) : M(M) {}
|
||||||
|
// CHECK: H(HMovable M) : M(std::move(M)) {}
|
||||||
|
|
||||||
|
// Try messing up with macros
|
||||||
|
#define MOVABLE_PARAM(Name) const Movable & Name
|
||||||
|
// CHECK: #define MOVABLE_PARAM(Name) const Movable & Name
|
||||||
|
struct I {
|
||||||
|
I(MOVABLE_PARAM(M)) : M(M) {}
|
||||||
|
// CHECK: I(MOVABLE_PARAM(M)) : M(M) {}
|
||||||
|
Movable M;
|
||||||
|
};
|
||||||
|
#undef MOVABLE_PARAM
|
||||||
|
|
||||||
|
// Test that templates aren't modified
|
||||||
|
template <typename T> struct J {
|
||||||
|
J(const T &M) : M(M) {}
|
||||||
|
// CHECK: J(const T &M) : M(M) {}
|
||||||
|
T M;
|
||||||
|
};
|
||||||
|
J<Movable> j1(Movable());
|
||||||
|
J<NotMovable> j2(NotMovable());
|
||||||
|
|
||||||
|
struct K_Movable {
|
||||||
|
K_Movable() = default;
|
||||||
|
K_Movable(const K_Movable &) = default;
|
||||||
|
K_Movable(K_Movable &&o) { dummy = o.dummy; }
|
||||||
|
int dummy;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test with movable type with an user defined move constructor.
|
||||||
|
struct K {
|
||||||
|
K(const K_Movable &M) : M(M) {}
|
||||||
|
// CHECK: K(K_Movable M) : M(std::move(M)) {}
|
||||||
|
K_Movable M;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct L {
|
||||||
|
L(const Movable &M) : M(M) {}
|
||||||
|
// CHECK: L(Movable M) : M(std::move(M)) {}
|
||||||
|
Movable M;
|
||||||
|
};
|
||||||
|
L<int> l(Movable());
|
||||||
|
|
||||||
|
// Test with a non-instantiated template class
|
||||||
|
template <typename T> struct N {
|
||||||
|
N(const Movable &M) : M(M) {}
|
||||||
|
// CHECK: N(Movable M) : M(std::move(M)) {}
|
||||||
|
|
||||||
|
Movable M;
|
||||||
|
T A;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test with value parameter
|
||||||
|
struct O {
|
||||||
|
O(Movable M) : M(M) {}
|
||||||
|
// CHECK: O(Movable M) : M(std::move(M)) {}
|
||||||
|
Movable M;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test with a const-value parameter
|
||||||
|
struct P {
|
||||||
|
P(const Movable M) : M(M) {}
|
||||||
|
// CHECK: P(Movable M) : M(std::move(M)) {}
|
||||||
|
Movable M;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test with multiples parameters where some need to be changed and some don't
|
||||||
|
// need to.
|
||||||
|
struct Q {
|
||||||
|
Q(const Movable &A, const Movable &B, const Movable &C, double D)
|
||||||
|
: A(A), B(B), C(C), D(D) {}
|
||||||
|
// CHECK: Q(const Movable &A, Movable B, Movable C, double D)
|
||||||
|
// CHECK-NEXT: : A(A), B(std::move(B)), C(std::move(C)), D(D) {}
|
||||||
|
const Movable &A;
|
||||||
|
Movable B;
|
||||||
|
Movable C;
|
||||||
|
double D;
|
||||||
|
};
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef BASIC_H
|
||||||
|
#define BASIC_H
|
||||||
|
|
||||||
|
// POD types are trivially move constructible
|
||||||
|
struct Movable {
|
||||||
|
int a, b, c;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NotMovable {
|
||||||
|
NotMovable() = default;
|
||||||
|
NotMovable(const NotMovable &) = default;
|
||||||
|
NotMovable(NotMovable &&) = delete;
|
||||||
|
int a, b, c;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The test runs the migrator without header modifications enabled for this
|
||||||
|
// header making the constructor parameter M unmodifiable.
|
||||||
|
struct UnmodifiableClass {
|
||||||
|
UnmodifiableClass(const Movable &M);
|
||||||
|
Movable M;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BASIC_H
|
Loading…
Reference in New Issue