diff options
Diffstat (limited to 'clang-tools-extra/cpp11-migrate/PassByValue/PassByValueActions.cpp')
-rw-r--r-- | clang-tools-extra/cpp11-migrate/PassByValue/PassByValueActions.cpp | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/clang-tools-extra/cpp11-migrate/PassByValue/PassByValueActions.cpp b/clang-tools-extra/cpp11-migrate/PassByValue/PassByValueActions.cpp new file mode 100644 index 00000000000..612850a70db --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/PassByValue/PassByValueActions.cpp @@ -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; +} |