/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
virtualvoid postRun() override
{ for (autoconst& pair : maVarDeclMap)
{ auto varDecl = pair.first; autoconst& depthInfo = pair.second; if (depthInfo.maDeclBlockPath.size() == depthInfo.maCommonBlockPath.size()) continue; if (maVarDeclToIgnoreSet.find(varDecl) != maVarDeclToIgnoreSet.end()) continue; auto it = maVarUseSourceRangeMap.find(varDecl); if (it == maVarUseSourceRangeMap.end()) continue;
report(DiagnosticsEngine::Warning, "can reduce scope of var", varDecl->getLocation())
<< varDecl->getSourceRange(); for (SourceRange const& useRange : it->second)
report(DiagnosticsEngine::Note, "used here", useRange.getBegin()) << useRange;
}
}
bool VisitUnaryOperator(UnaryOperator const* expr)
{ // if we take the address of it
UnaryOperator::Opcode op = expr->getOpcode(); if (op == UO_AddrOf)
recordIgnore(expr->getSubExpr()); returntrue;
}
void recordIgnore(Expr const* expr)
{ for (;;)
{
expr = expr->IgnoreParenImpCasts(); if (autoconst e = dyn_cast<MemberExpr>(expr))
{ if (isa<FieldDecl>(e->getMemberDecl()))
{
expr = e->getBase(); continue;
}
} if (autoconst e = dyn_cast<ArraySubscriptExpr>(expr))
{
expr = e->getBase(); continue;
} if (autoconst e = dyn_cast<BinaryOperator>(expr))
{ if (e->getOpcode() == BO_PtrMemD)
{
expr = e->getLHS(); continue;
}
} break;
} autoconst dre = dyn_cast<DeclRefExpr>(expr); if (dre == nullptr) return; autoconst var = dyn_cast<VarDecl>(dre->getDecl()); if (var == nullptr) return;
maVarDeclToIgnoreSet.insert(var);
}
};
unsignedint ReduceVarScope::gnBlockId = 0;
bool ReduceVarScope::PreTraverseFunctionDecl(FunctionDecl* functionDecl)
{ // Ignore functions that contains #ifdef-ery, can be quite tricky // to make useful changes when this plugin fires in such functions if (containsPreprocessingConditionalInclusion(functionDecl->getSourceRange())) returnfalse; returntrue;
}
// Consider a switch to be a loop, because weird things happen inside it bool ReduceVarScope::TraverseSwitchStmt(SwitchStmt* decl)
{ bool ret = true; if (PreTraverseSwitchStmt(decl))
{
ret = FilteringPlugin::TraverseSwitchStmt(decl);
PostTraverseSwitchStmt(decl, ret);
} return ret;
}
bool ReduceVarScope::TraverseForStmt(ForStmt* decl)
{ bool ret = true; if (PreTraverseForStmt(decl))
{
ret = FilteringPlugin::TraverseForStmt(decl);
PostTraverseForStmt(decl, ret);
} return ret;
}
bool ReduceVarScope::VisitVarDecl(const VarDecl* varDecl)
{ if (ignoreLocation(varDecl)) returntrue; if (varDecl->isExceptionVariable() || isa<ParmVarDecl>(varDecl)) returntrue; // ignore stuff in header files (which should really not be there, but anyhow) if (!compiler.getSourceManager().isInMainFile(varDecl->getLocation())) returntrue; // Ignore macros like FD_ZERO if (compiler.getSourceManager().isMacroBodyExpansion(varDecl->getBeginLoc())) returntrue; if (varDecl->hasGlobalStorage()) returntrue; if (varDecl->isConstexpr()) returntrue; if (varDecl->isInitCapture()) returntrue; if (varDecl->isCXXForRangeDecl()) returntrue; if (!isTypeOK(varDecl->getType())) returntrue;
if (varDecl->hasInit() && !isInitConstant(varDecl)) returntrue;
bool ReduceVarScope::isTypeOK(QualType varType)
{ // TODO improve this - requires more analysis because it's really easy to // take a pointer to an array if (varType->isArrayType()) returnfalse;
if (varType.isCXX11PODType(compiler.getASTContext())) returntrue; if (!varType->isRecordType()) returnfalse; auto recordDecl = dyn_cast_or_null<CXXRecordDecl>(varType->getAs<RecordType>()->getDecl()); if (recordDecl && recordDecl->hasTrivialDestructor()) returntrue; autoconst tc = loplugin::TypeCheck(varType); // Safe types with destructors that don't do anything interesting if (tc.Class("OString").Namespace("rtl").GlobalNamespace()
|| tc.Class("OUString").Namespace("rtl").GlobalNamespace()
|| tc.Class("OStringBuffer").Namespace("rtl").GlobalNamespace()
|| tc.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
|| tc.Class("Color").GlobalNamespace() || tc.Class("Pair").GlobalNamespace()
|| tc.Class("Point").GlobalNamespace() || tc.Class("Size").GlobalNamespace()
|| tc.Class("Range").GlobalNamespace() || tc.Class("Selection").GlobalNamespace()
|| tc.Class("Rectangle").Namespace("tools").GlobalNamespace()) returntrue; returnfalse;
}
bool ReduceVarScope::VisitDeclRefExpr(const DeclRefExpr* declRefExpr)
{ if (ignoreLocation(declRefExpr)) returntrue; const Decl* decl = declRefExpr->getDecl(); if (!isa<VarDecl>(decl) || isa<ParmVarDecl>(decl)) returntrue; const VarDecl* varDecl = dyn_cast<VarDecl>(decl)->getCanonicalDecl(); // ignore stuff in header files (which should really not be there, but anyhow) if (!compiler.getSourceManager().isInMainFile(varDecl->getLocation())) returntrue;
auto varIt = maVarDeclMap.find(varDecl); if (varIt == maVarDeclMap.end()) returntrue;
auto& depthInfo = varIt->second;
// merge block paths to get common ancestor path if (depthInfo.maCommonBlockPath.empty())
depthInfo.maCommonBlockPath = maCurrentBlockPath; else
{ auto len = std::min(depthInfo.maCommonBlockPath.size(), maCurrentBlockPath.size()); unsignedint i = 0; while (i < len && depthInfo.maCommonBlockPath[i] == maCurrentBlockPath[i])
++i;
depthInfo.maCommonBlockPath.resize(i); if (depthInfo.maCommonBlockPath == depthInfo.maDeclBlockPath)
{
maVarDeclMap.erase(varIt);
maVarUseSourceRangeMap.erase(varDecl); returntrue;
}
}
// seen in a loop below initial decl if (mnCurrentLoopDepth > depthInfo.mnFirstLoopDepth)
{ // TODO, we could additionally check if we are reading or writing to the var inside a loop // We only need to exclude vars that are written to, or passed taken-addr-of, or have non-const method called, // or passed as arg to non-const-ref parameter.
maVarDeclMap.erase(varIt);
maVarUseSourceRangeMap.erase(varDecl); returntrue;
}
auto it = maVarUseSourceRangeMap.find(varDecl); if (it == maVarUseSourceRangeMap.end())
it = maVarUseSourceRangeMap.emplace(varDecl, std::vector<SourceRange>()).first;
it->second.push_back(declRefExpr->getSourceRange());
returntrue;
}
bool ReduceVarScope::VisitLambdaExpr(const LambdaExpr* lambdaExpr)
{ if (ignoreLocation(lambdaExpr)) returntrue; for (auto captureIt = lambdaExpr->capture_begin(); captureIt != lambdaExpr->capture_end();
++captureIt)
{ const LambdaCapture& capture = *captureIt; if (capture.capturesVariable())
{ auto varDecl = cast<VarDecl>(capture.getCapturedVar());
maVarDeclMap.erase(varDecl);
maVarUseSourceRangeMap.erase(varDecl);
}
} returntrue;
}
¤ 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.0.16Bemerkung:
(vorverarbeitet)
¤
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.