forked from OSchip/llvm-project
176 lines
6.4 KiB
C++
176 lines
6.4 KiB
C++
//===-- 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;
|
|
}
|