summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/cpp11-migrate/PassByValue/PassByValueActions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/cpp11-migrate/PassByValue/PassByValueActions.cpp')
-rw-r--r--clang-tools-extra/cpp11-migrate/PassByValue/PassByValueActions.cpp167
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 &param' -> '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;
+}
OpenPOWER on IntegriCloud