/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * Based on LLVM/Clang. * * This file is distributed under the University of Illinois Open Source * License. See LICENSE.TXT for details. *
*/
/* Look for local variables that can be std::move'd into parameters.
TODO (*) Ideally we would use a proper data-flow analysis, to detect that the var is dead after this point, like the one in clang at include/clang/Analysis/CFG.h (*) we could expand the set of approved/interesting types
*/
namespace
{ class MoveIt : public loplugin::FilteringPlugin<MoveIt>
{ public: explicit MoveIt(loplugin::InstantiationData const& data)
: FilteringPlugin(data)
{
}
virtualbool preRun() override
{
std::string fn(handler.getMainFileName());
loplugin::normalizeDotDotInFilePath(fn); // false +, needs to check if the moved-from var is outside a loop if (loplugin::hasPathnamePrefix(
fn, SRCDIR "/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx")) returnfalse; if (loplugin::hasPathnamePrefix(
fn, SRCDIR "/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx")) returnfalse; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/drawinglayer/source/tools/emfphelperdata.cxx")) returnfalse; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/source/core/tool/reftokenhelper.cxx")) returnfalse; if (loplugin::hasPathnamePrefix(fn,
SRCDIR "/svx/source/svdraw/svdotextpathdecomposition.cxx")) returnfalse; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/svx/source/svdraw/svdcrtv.cxx")) returnfalse; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/svx/source/table/tablehandles.cxx")) returnfalse; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/svx/source/xoutdev/xpool.cxx")) returnfalse; returntrue;
}
virtualvoid run() override
{ if (preRun())
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); for (autoconst& pair : m_possibles)
{ autoconst& possible = pair.second;
report(DiagnosticsEngine::Warning, "can std::move this var into this param",
possible.argExpr->getBeginLoc());
report(DiagnosticsEngine::Note, "passing to this param",
possible.calleeParmVarDecl->getBeginLoc());
report(DiagnosticsEngine::Note, "local var declared here",
possible.localVarDecl->getBeginLoc());
report(DiagnosticsEngine::Note, "type declared here",
possible.recordDecl->getBeginLoc());
}
}
bool MoveIt::VisitCXXMemberCallExpr(const CXXMemberCallExpr* topExpr)
{ if (ignoreLocation(topExpr)) returntrue; const CXXMethodDecl* methodDecl = topExpr->getMethodDecl(); if (!methodDecl) returntrue;
unsigned len = std::min(topExpr->getNumArgs(), methodDecl->getNumParams()); for (unsigned i = 0; i < len; ++i)
{ // check if the parameter is a moveable type const ParmVarDecl* parmVarDecl = methodDecl->getParamDecl(i); if (!parmVarDecl->getType()->isRecordType()) continue; const CXXRecordDecl* recordDecl
= dyn_cast<CXXRecordDecl>(parmVarDecl->getType()->getAsRecordDecl()); if (!recordDecl || !recordDecl->hasMoveConstructor() || recordDecl->isTriviallyCopyable()) continue; if (!isInterestingType(parmVarDecl->getType())) continue;
// check if (a) we're making a copy to pass to the param and (b) we're making a copy of a local var const Expr* argExpr = topExpr->getArg(i); if (!argExpr) continue; const CXXConstructExpr* argSubExpr = dyn_cast<CXXConstructExpr>(argExpr->IgnoreImplicit()); if (!argSubExpr || argSubExpr->getNumArgs() == 0) continue; const DeclRefExpr* dre = dyn_cast<DeclRefExpr>(argSubExpr->getArg(0)->IgnoreImplicit()); if (!dre) continue; const VarDecl* localVarDecl = dyn_cast<VarDecl>(dre->getDecl()); if (!localVarDecl || localVarDecl->getType()->isReferenceType()
|| localVarDecl->getType()->isPointerType() || !localVarDecl->hasLocalStorage()) continue; // because sometimes the parameter type is some obscured STL thing if (!isInterestingType(localVarDecl->getType())) continue;
if (m_rejected.count(localVarDecl)) continue;
m_possibles[localVarDecl] = Possible{ argExpr, parmVarDecl, localVarDecl, recordDecl, dre };
}
returntrue;
}
bool MoveIt::VisitCXXConstructExpr(const CXXConstructExpr* topExpr)
{ if (ignoreLocation(topExpr)) returntrue; if (isa<CXXTemporaryObjectExpr>(topExpr)) returntrue; const CXXConstructorDecl* methodDecl = topExpr->getConstructor(); if (!methodDecl) returntrue;
unsigned len = std::min(topExpr->getNumArgs(), methodDecl->getNumParams()); for (unsigned i = 0; i < len; ++i)
{ // check if the parameter is a moveable type const ParmVarDecl* parmVarDecl = methodDecl->getParamDecl(i); if (!parmVarDecl->getType()->isRecordType()) continue; const CXXRecordDecl* recordDecl
= dyn_cast<CXXRecordDecl>(parmVarDecl->getType()->getAsRecordDecl()); if (!recordDecl || !recordDecl->hasMoveConstructor() || recordDecl->isTriviallyCopyable()) continue; if (!isInterestingType(parmVarDecl->getType())) continue;
// check if (a) we're making a copy to pass to the param and (b) we're making a copy of a local var const Expr* argExpr = topExpr->getArg(i); if (!argExpr) continue; const CXXConstructExpr* argSubExpr = dyn_cast<CXXConstructExpr>(argExpr->IgnoreImplicit()); if (!argSubExpr || argSubExpr->getNumArgs() == 0) continue; const DeclRefExpr* dre = dyn_cast<DeclRefExpr>(argSubExpr->getArg(0)->IgnoreImplicit()); if (!dre) continue; const VarDecl* localVarDecl = dyn_cast<VarDecl>(dre->getDecl()); if (!localVarDecl || localVarDecl->getType()->isReferenceType()
|| localVarDecl->getType()->isPointerType() || !localVarDecl->hasLocalStorage()) continue; // because sometimes the parameter type is some obscured STL thing if (!isInterestingType(localVarDecl->getType())) continue;
if (m_rejected.count(localVarDecl)) continue;
m_possibles[localVarDecl] = Possible{ argExpr, parmVarDecl, localVarDecl, recordDecl, dre };
}
returntrue;
}
/// If we have pushed a possibility, and then we see that possibility again, /// then we cannot std::move it, because it is being referenced after being moved. /// bool MoveIt::VisitDeclRefExpr(const DeclRefExpr* declRefExpr)
{ if (ignoreLocation(declRefExpr)) returntrue; const VarDecl* localVarDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()); if (!localVarDecl) returntrue; auto it = m_possibles.find(localVarDecl); if (it == m_possibles.end()) returntrue; // ignoring the DeclRefExpr* for the expression where we found the Possible if (it->second.dre == declRefExpr) returntrue;
m_possibles.erase(it);
m_rejected.insert(localVarDecl); returntrue;
}
/// Exclude boring types, so that we don't generate too many low-value conversions. /// e.g. for now I ignore ref-counted types like Sequence and OUString and css::uno::Reference, /// because that generates too many changes bool MoveIt::isInterestingType(QualType qt)
{ if (qt->isEnumeralType()) returnfalse; if (!qt->isRecordType()) returnfalse;
/// off by default because each warning needs to be hand checked to ensure it is not a false+
loplugin::Plugin::Registration<MoveIt> moveit("moveit", false);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.