| //===-- 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 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; |
| } |