/* -*- 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/.
*/
// try to limit the voluminous output a little static std::set<MyVarInfo> readFromSet; static std::set<MyVarInfo> writeToSet; static std::set<MyVarInfo> definitionSet;
/** * Wrap the different kinds of callable and callee objects in the clang AST so I can define methods that handle everything.
*/ class CallerWrapper
{ const CallExpr* m_callExpr; const CXXConstructExpr* m_cxxConstructExpr;
// For reasons I do not understand, parentFunctionDecl() is not reliable, so // we store the parent function on the way down the AST.
FunctionDecl* insideFunctionDecl = nullptr;
std::vector<VarDecl const*> insideConditionalCheckOfVarSet;
};
aInfo.fieldName = varDecl->getNameAsString(); // sometimes the name (if it's an anonymous thing) contains the full path of the build folder, which we don't need
size_t idx = aInfo.fieldName.find(SRCDIR); if (idx != std::string::npos)
{
aInfo.fieldName = aInfo.fieldName.replace(idx, strlen(SRCDIR), "");
}
aInfo.fieldType = varDecl->getType().getAsString();
bool UnusedVarsGlobal::VisitVarDecl(const VarDecl* varDecl)
{
varDecl = varDecl->getCanonicalDecl(); if (isa<ParmVarDecl>(varDecl)) returntrue; if (!varDecl->hasGlobalStorage()) returntrue; if (!varDecl->getLocation().isValid() || ignoreLocation(varDecl)) returntrue; // ignore stuff that forms part of the stable URE interface if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(varDecl->getLocation()))) returntrue;
/** If we have const size_t NB_PRODUCTS = 3; int DefaultProductDir[NB_PRODUCTS] = { 3, 3, 3 }; clang will inline the constant "3" and never tell us that we are reading from NB_PRODUCTS, so just ignore integer constants.
*/ auto varType = varDecl->getType(); if (varType.isConstQualified() && varType->isIntegerType()) returntrue;
auto initExpr = varDecl->getAnyInitializer(); if (initExpr && !isSomeKindOfZero(initExpr))
writeToSet.insert(niceName(varDecl));
bool UnusedVarsGlobal::VisitDeclRefExpr(const DeclRefExpr* declRefExpr)
{ const Decl* decl = declRefExpr->getDecl(); const VarDecl* varDecl = dyn_cast<VarDecl>(decl); if (!varDecl) returntrue; if (isa<ParmVarDecl>(varDecl)) returntrue; if (!varDecl->hasGlobalStorage()) returntrue;
varDecl = varDecl->getCanonicalDecl(); if (!varDecl->getLocation().isValid() || ignoreLocation(varDecl)) returntrue; // ignore stuff that forms part of the stable URE interface if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(varDecl->getLocation()))) returntrue;
checkIfReadFrom(varDecl, declRefExpr);
checkIfWrittenTo(varDecl, declRefExpr); returntrue;
}
/** Does the expression being used to initialise a field value evaluate to the same as a default value?
*/ bool UnusedVarsGlobal::isSomeKindOfZero(const Expr* arg)
{
assert(arg); if (arg->isValueDependent()) returnfalse; if (arg->getType().isNull()) returnfalse; if (isa<CXXDefaultArgExpr>(arg))
arg = dyn_cast<CXXDefaultArgExpr>(arg)->getExpr();
arg = arg->IgnoreParenCasts(); // ignore this, it seems to trigger an infinite recursion if (isa<UnaryExprOrTypeTraitExpr>(arg)) returnfalse; if (auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(arg)) return cxxConstructExpr->getConstructor()->isDefaultConstructor();
APSInt x1; if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext())) return x1 == 0; if (isa<CXXNullPtrLiteralExpr>(arg)) returntrue; if (isa<MaterializeTemporaryExpr>(arg))
{ const CXXBindTemporaryExpr* strippedArg
= dyn_cast_or_null<CXXBindTemporaryExpr>(arg->IgnoreParenCasts()); if (strippedArg)
{ auto temp = dyn_cast<CXXTemporaryObjectExpr>(strippedArg->getSubExpr()); if (temp->getNumArgs() == 0)
{ if (loplugin::TypeCheck(temp->getType())
.Class("OUString")
.Namespace("rtl")
.GlobalNamespace()) returntrue; if (loplugin::TypeCheck(temp->getType())
.Class("OString")
.Namespace("rtl")
.GlobalNamespace()) returntrue; returnfalse;
}
}
}
// Get the expression contents. // This helps us find params which are always initialised with something like "OUString()".
SourceManager& SM = compiler.getSourceManager();
SourceLocation startLoc = arg->getBeginLoc();
SourceLocation endLoc = arg->getEndLoc(); constchar* p1 = SM.getCharacterData(startLoc); constchar* p2 = SM.getCharacterData(endLoc); if (!p1 || !p2 || (p2 - p1) < 0 || (p2 - p1) > 40) returnfalse; unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts());
std::string s(p1, p2 - p1 + n); // strip linefeed and tab characters so they don't interfere with the parsing of the log file
std::replace(s.begin(), s.end(), '\r', ' ');
std::replace(s.begin(), s.end(), '\n', ' ');
std::replace(s.begin(), s.end(), '\t', ' ');
// now normalize the value. For some params, like OUString, we can pass it as OUString() or "" and they are the same thing if (s == "OUString()") returntrue; elseif (s == "OString()") returntrue; returnfalse;
}
staticchar easytolower(char in)
{ if (in <= 'Z' && in >= 'A') return in - ('Z' - 'z'); return in;
}
if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(cond))
{ if (auto cxxConvert = dyn_cast_or_null<CXXConversionDecl>(memberCallExpr->getMethodDecl()))
{ if (cxxConvert->getConversionType()->isBooleanType()) if (auto declRefExpr = dyn_cast<DeclRefExpr>(
memberCallExpr->getImplicitObjectArgument()->IgnoreParenImpCasts())) if ((varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl())))
insideConditionalCheckOfVarSet.push_back(varDecl);
}
} elseif (auto declRefExpr = dyn_cast<DeclRefExpr>(cond))
{ if ((varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl())))
insideConditionalCheckOfVarSet.push_back(varDecl);
}
bool ret = RecursiveASTVisitor::TraverseIfStmt(ifStmt); if (varDecl)
insideConditionalCheckOfVarSet.pop_back(); return ret;
}
void UnusedVarsGlobal::checkIfReadFrom(const VarDecl* varDecl, const DeclRefExpr* declRefExpr)
{ auto parentsRange = compiler.getASTContext().getParents(*declRefExpr); const Stmt* child = declRefExpr; const Stmt* parent
= parentsRange.begin() == parentsRange.end() ? nullptr : parentsRange.begin()->get<Stmt>(); // walk up the tree until we find something interesting bool bPotentiallyReadFrom = false; bool bDump = false; auto walkUp = [&]() {
child = parent; auto parentsRange = compiler.getASTContext().getParents(*parent);
parent = parentsRange.begin() == parentsRange.end() ? nullptr
: parentsRange.begin()->get<Stmt>();
}; do
{ if (!parent)
{ // check if we're inside a CXXCtorInitializer or a VarDecl auto parentsRange = compiler.getASTContext().getParents(*child); if (parentsRange.begin() != parentsRange.end())
{ const Decl* decl = parentsRange.begin()->get<Decl>(); if (decl && (isa<CXXConstructorDecl>(decl) || isa<VarDecl>(decl)))
bPotentiallyReadFrom = true;
} if (!bPotentiallyReadFrom) return; break;
} if (isa<CXXReinterpretCastExpr>(parent))
{ // once we see one of these, there is not much useful we can know
bPotentiallyReadFrom = true; break;
} elseif (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent)
|| isa<ParenListExpr>(parent) || isa<ArrayInitLoopExpr>(parent)
|| isa<ExprWithCleanups>(parent))
{
walkUp();
} elseif (auto unaryOperator = dyn_cast<UnaryOperator>(parent))
{
UnaryOperator::Opcode op = unaryOperator->getOpcode(); if (declRefExpr->getType()->isArrayType() && op == UO_Deref)
{ // ignore, deref'ing an array does not count as a read
} elseif (op == UO_AddrOf || op == UO_Deref || op == UO_Plus || op == UO_Minus
|| op == UO_Not || op == UO_LNot)
{
bPotentiallyReadFrom = true; break;
} /* The following are technically reads, but from a code-sense they're more of a write/modify, so ignore them to find interesting fields that only modified, not usefully read: UO_PreInc / UO_PostInc / UO_PreDec / UO_PostDec But we still walk up in case the result of the expression is used in a read sense.
*/
walkUp();
} elseif (auto caseStmt = dyn_cast<CaseStmt>(parent))
{
bPotentiallyReadFrom = caseStmt->getLHS() == child || caseStmt->getRHS() == child; break;
} elseif (auto ifStmt = dyn_cast<IfStmt>(parent))
{
bPotentiallyReadFrom = ifStmt->getCond() == child; break;
} elseif (auto doStmt = dyn_cast<DoStmt>(parent))
{
bPotentiallyReadFrom = doStmt->getCond() == child; break;
} elseif (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent))
{ if (arraySubscriptExpr->getIdx() == child)
{
bPotentiallyReadFrom = true; break;
}
walkUp();
} elseif (auto binaryOp = dyn_cast<BinaryOperator>(parent))
{
BinaryOperator::Opcode op = binaryOp->getOpcode(); constbool assignmentOp = op == BO_Assign || op == BO_MulAssign || op == BO_DivAssign
|| op == BO_RemAssign || op == BO_AddAssign
|| op == BO_SubAssign || op == BO_ShlAssign
|| op == BO_ShrAssign || op == BO_AndAssign
|| op == BO_XorAssign || op == BO_OrAssign; if (binaryOp->getLHS() == child && assignmentOp) break; else
{
bPotentiallyReadFrom = true; break;
}
} elseif (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent))
{ auto op = operatorCallExpr->getOperator(); constbool assignmentOp = op == OO_Equal || op == OO_StarEqual || op == OO_SlashEqual
|| op == OO_PercentEqual || op == OO_PlusEqual
|| op == OO_MinusEqual || op == OO_LessLessEqual
|| op == OO_AmpEqual || op == OO_CaretEqual
|| op == OO_PipeEqual; if (operatorCallExpr->getArg(0) == child && assignmentOp) break; elseif (op == OO_GreaterGreaterEqual && operatorCallExpr->getArg(1) == child) break; // this is a write-only call else
{
bPotentiallyReadFrom = true; break;
}
} elseif (auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(parent))
{ bool bWriteOnlyCall = false; const CXXMethodDecl* callee = cxxMemberCallExpr->getMethodDecl(); if (callee)
{ const Expr* tmp = dyn_cast<Expr>(child); if (tmp->isBoundMemberFunction(compiler.getASTContext()))
{
tmp = dyn_cast<MemberExpr>(tmp)->getBase();
} if (cxxMemberCallExpr->getImplicitObjectArgument() == tmp)
{ // FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute // which we could scatter around.
std::string name = callee->getNameAsString();
std::transform(name.begin(), name.end(), name.begin(), easytolower); if (startswith(name, "emplace") || name == "insert" || name == "erase"
|| name == "remove" || name == "remove_if" || name == "sort"
|| name == "push_back" || name == "pop_back" || name == "push_front"
|| name == "pop_front" || name == "reserve" || name == "resize"
|| name == "reset" || name == "clear" || name == "fill") // write-only modifications to collections
bWriteOnlyCall = true; elseif (name == "dispose" || name == "disposeAndClear" || name == "swap") // we're abusing the write-only analysis here to look for fields which don't have anything useful // being done to them, so we're ignoring things like std::vector::clear, std::vector::swap, // and VclPtr::disposeAndClear
bWriteOnlyCall = true;
}
} if (!bWriteOnlyCall)
bPotentiallyReadFrom = true; break;
} elseif (auto callExpr = dyn_cast<CallExpr>(parent))
{ bool bWriteOnlyCall = false; // check for calls to ReadXXX(foo) type methods, where foo is write-only auto callee = getCallee(callExpr); if (callee)
{ // FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute // which we could scatter around.
std::string name = callee->getNameAsString();
std::transform(name.begin(), name.end(), name.begin(), easytolower); if (startswith(name, "read")) // this is a write-only call
bWriteOnlyCall = true;
} if (!bWriteOnlyCall)
bPotentiallyReadFrom = true; break;
} elseif (isa<ReturnStmt>(parent) || isa<CXXConstructExpr>(parent)
|| isa<ConditionalOperator>(parent) || isa<SwitchStmt>(parent)
|| isa<DeclStmt>(parent) || isa<WhileStmt>(parent) || isa<CXXNewExpr>(parent)
|| isa<ForStmt>(parent) || isa<InitListExpr>(parent)
|| isa<CXXDependentScopeMemberExpr>(parent) || isa<UnresolvedMemberExpr>(parent)
|| isa<MaterializeTemporaryExpr>(parent))
{
bPotentiallyReadFrom = true; break;
} elseif (isa<CXXDeleteExpr>(parent) || isa<UnaryExprOrTypeTraitExpr>(parent)
|| isa<CXXUnresolvedConstructExpr>(parent) || isa<CompoundStmt>(parent)
|| isa<LabelStmt>(parent) || isa<CXXForRangeStmt>(parent)
|| isa<CXXTypeidExpr>(parent) || isa<DefaultStmt>(parent)
|| isa<GCCAsmStmt>(parent) || isa<LambdaExpr>(parent) // TODO
|| isa<CXXDefaultArgExpr>(parent) || isa<AtomicExpr>(parent)
|| isa<VAArgExpr>(parent) || isa<DeclRefExpr>(parent) || isa<ConstantExpr>(parent)
|| isa<SubstNonTypeTemplateParmExpr>(parent))
{ break;
} else
{
bPotentiallyReadFrom = true;
bDump = true; break;
}
} while (true);
if (bDump)
{
report(DiagnosticsEngine::Warning, "oh dear, what can the matter be?",
declRefExpr->getBeginLoc())
<< declRefExpr->getSourceRange();
report(DiagnosticsEngine::Note, "parent over here", parent->getBeginLoc())
<< parent->getSourceRange();
parent->dump();
declRefExpr->dump();
}
if (bPotentiallyReadFrom)
readFromSet.insert(niceName(varDecl));
}
void UnusedVarsGlobal::checkIfWrittenTo(const VarDecl* varDecl, const DeclRefExpr* declRefExpr)
{ // if we're inside a block that looks like // if (varDecl) // ... // then writes to this field don't matter, because unless we find another write to this field, this field is dead if (std::find(insideConditionalCheckOfVarSet.begin(), insideConditionalCheckOfVarSet.end(),
varDecl)
!= insideConditionalCheckOfVarSet.end()) return;
auto parentsRange = compiler.getASTContext().getParents(*declRefExpr); const Stmt* child = declRefExpr; const Stmt* parent
= parentsRange.begin() == parentsRange.end() ? nullptr : parentsRange.begin()->get<Stmt>(); // walk up the tree until we find something interesting bool bPotentiallyWrittenTo = false; bool bDump = false; auto walkUp = [&]() {
child = parent; auto parentsRange = compiler.getASTContext().getParents(*parent);
parent = parentsRange.begin() == parentsRange.end() ? nullptr
: parentsRange.begin()->get<Stmt>();
}; do
{ if (!parent)
{ // check if we have an expression like // int& r = m_field; auto parentsRange = compiler.getASTContext().getParents(*child); if (parentsRange.begin() != parentsRange.end())
{ auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>()); // The isImplicit() call is to avoid triggering when we see the vardecl which is part of a for-range statement, // which is of type 'T&&' and also an l-value-ref ? if (varDecl && !varDecl->isImplicit()
&& loplugin::TypeCheck(varDecl->getType()).LvalueReference().NonConst())
{
bPotentiallyWrittenTo = true;
}
} break;
} if (isa<CXXReinterpretCastExpr>(parent))
{ // once we see one of these, there is not much useful we can know
bPotentiallyWrittenTo = true; break;
} elseif (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent)
|| isa<ParenListExpr>(parent) || isa<ArrayInitLoopExpr>(parent)
|| isa<ExprWithCleanups>(parent))
{
walkUp();
} elseif (auto unaryOperator = dyn_cast<UnaryOperator>(parent))
{
UnaryOperator::Opcode op = unaryOperator->getOpcode(); if (op == UO_AddrOf || op == UO_PostInc || op == UO_PostDec || op == UO_PreInc
|| op == UO_PreDec)
{
bPotentiallyWrittenTo = true;
} break;
} elseif (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent))
{ if (arraySubscriptExpr->getIdx() == child) break;
walkUp();
} elseif (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent))
{ auto callee = getCallee(operatorCallExpr); if (callee)
{ // if calling a non-const operator on the field auto calleeMethodDecl = callee->getAsCXXMethodDecl(); if (calleeMethodDecl && operatorCallExpr->getArg(0) == child)
{ if (!calleeMethodDecl->isConst())
bPotentiallyWrittenTo
= checkForWriteWhenUsingCollectionType(calleeMethodDecl);
} elseif (IsPassedByNonConst(varDecl, child, operatorCallExpr, *callee))
{
bPotentiallyWrittenTo = true;
}
} else
bPotentiallyWrittenTo = true; // conservative, could improve break;
} elseif (auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(parent))
{ const CXXMethodDecl* calleeMethodDecl = cxxMemberCallExpr->getMethodDecl(); if (calleeMethodDecl)
{ // if calling a non-const method on the field const Expr* tmp = dyn_cast<Expr>(child); if (tmp->isBoundMemberFunction(compiler.getASTContext()))
{
tmp = dyn_cast<MemberExpr>(tmp)->getBase();
} if (cxxMemberCallExpr->getImplicitObjectArgument() == tmp)
{ if (!calleeMethodDecl->isConst())
bPotentiallyWrittenTo
= checkForWriteWhenUsingCollectionType(calleeMethodDecl); break;
} elseif (IsPassedByNonConst(varDecl, child, cxxMemberCallExpr,
CalleeWrapper(calleeMethodDecl)))
bPotentiallyWrittenTo = true;
} else
bPotentiallyWrittenTo = true; // can happen in templates break;
} elseif (auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(parent))
{ if (IsPassedByNonConst(varDecl, child, cxxConstructExpr,
CalleeWrapper(cxxConstructExpr)))
bPotentiallyWrittenTo = true; break;
} elseif (auto callExpr = dyn_cast<CallExpr>(parent))
{ auto callee = getCallee(callExpr); if (callee)
{ if (IsPassedByNonConst(varDecl, child, callExpr, *callee))
bPotentiallyWrittenTo = true;
} else
bPotentiallyWrittenTo = true; // conservative, could improve break;
} elseif (auto binaryOp = dyn_cast<BinaryOperator>(parent))
{
BinaryOperator::Opcode op = binaryOp->getOpcode(); constbool assignmentOp = op == BO_Assign || op == BO_MulAssign || op == BO_DivAssign
|| op == BO_RemAssign || op == BO_AddAssign
|| op == BO_SubAssign || op == BO_ShlAssign
|| op == BO_ShrAssign || op == BO_AndAssign
|| op == BO_XorAssign || op == BO_OrAssign; if (assignmentOp)
{ if (binaryOp->getLHS() == child)
bPotentiallyWrittenTo = true; elseif (loplugin::TypeCheck(binaryOp->getLHS()->getType())
.LvalueReference()
.NonConst()) // if the LHS is a non-const reference, we could write to the field later on
bPotentiallyWrittenTo = true;
} break;
} elseif (isa<ReturnStmt>(parent))
{ if (insideFunctionDecl)
{ auto tc = loplugin::TypeCheck(insideFunctionDecl->getReturnType()); if (tc.LvalueReference().NonConst())
bPotentiallyWrittenTo = true;
} break;
} elseif (isa<ConditionalOperator>(parent) || isa<SwitchStmt>(parent)
|| isa<DeclStmt>(parent) || isa<WhileStmt>(parent) || isa<CXXNewExpr>(parent)
|| isa<ForStmt>(parent) || isa<InitListExpr>(parent)
|| isa<CXXDependentScopeMemberExpr>(parent) || isa<UnresolvedMemberExpr>(parent)
|| isa<MaterializeTemporaryExpr>(parent) || isa<IfStmt>(parent)
|| isa<DoStmt>(parent) || isa<CXXDeleteExpr>(parent)
|| isa<UnaryExprOrTypeTraitExpr>(parent) || isa<CXXUnresolvedConstructExpr>(parent)
|| isa<CompoundStmt>(parent) || isa<LabelStmt>(parent)
|| isa<CXXForRangeStmt>(parent) || isa<CXXTypeidExpr>(parent)
|| isa<DefaultStmt>(parent) || isa<GCCAsmStmt>(parent) || isa<ConstantExpr>(parent)
|| isa<AtomicExpr>(parent) || isa<CXXDefaultArgExpr>(parent)
|| isa<VAArgExpr>(parent) || isa<DeclRefExpr>(parent)
|| isa<SubstNonTypeTemplateParmExpr>(parent) || isa<LambdaExpr>(parent)) // TODO
{ break;
} else
{
bPotentiallyWrittenTo = true;
bDump = true; break;
}
} while (true);
if (bDump)
{
report(DiagnosticsEngine::Warning, "oh dear, what can the matter be? writtenTo=%0",
declRefExpr->getBeginLoc())
<< bPotentiallyWrittenTo << declRefExpr->getSourceRange(); if (parent)
{
report(DiagnosticsEngine::Note, "parent over here", parent->getBeginLoc())
<< parent->getSourceRange();
parent->dump();
}
declRefExpr->dump();
varDecl->getType()->dump();
}
if (bPotentiallyWrittenTo)
writeToSet.insert(niceName(varDecl));
}
// return true if this not a collection type, or if it is a collection type, and we might be writing to it bool UnusedVarsGlobal::checkForWriteWhenUsingCollectionType(const CXXMethodDecl* calleeMethodDecl)
{ autoconst tc = loplugin::TypeCheck(calleeMethodDecl->getParent()); bool listLike = false, setLike = false, mapLike = false, cssSequence = false; if (tc.Class("deque").StdNamespace() || tc.Class("list").StdNamespace()
|| tc.Class("queue").StdNamespace() || tc.Class("vector").StdNamespace())
{
listLike = true;
} elseif (tc.Class("set").StdNamespace() || tc.Class("unordered_set").StdNamespace())
{
setLike = true;
} elseif (tc.Class("map").StdNamespace() || tc.Class("unordered_map").StdNamespace())
{
mapLike = true;
} elseif (tc.Class("Sequence")
.Namespace("uno")
.Namespace("star")
.Namespace("sun")
.Namespace("com")
.GlobalNamespace())
{
cssSequence = true;
} else returntrue;
if (calleeMethodDecl->isOverloadedOperator())
{ auto oo = calleeMethodDecl->getOverloadedOperator(); if (oo == OO_Equal) returntrue; // This is operator[]. We only care about things that add elements to the collection. // if nothing modifies the size of the collection, then nothing useful // is stored in it. if (listLike) returnfalse; returntrue;
}
auto name = calleeMethodDecl->getName(); if (listLike || setLike || mapLike)
{ if (name == "reserve" || name == "shrink_to_fit" || name == "clear" || name == "erase"
|| name == "pop_back" || name == "pop_front" || name == "front" || name == "back"
|| name == "data" || name == "remove" || name == "remove_if" || name == "unique"
|| name == "sort" || name == "begin" || name == "end" || name == "rbegin"
|| name == "rend" || name == "at" || name == "find" || name == "equal_range"
|| name == "lower_bound" || name == "upper_bound") returnfalse;
} if (cssSequence)
{ if (name == "getArray" || name == "begin" || name == "end") returnfalse;
}
returntrue;
}
bool UnusedVarsGlobal::IsPassedByNonConst(const VarDecl* varDecl, const Stmt* child,
CallerWrapper callExpr, CalleeWrapper calleeFunctionDecl)
{ unsigned len = std::min(callExpr.getNumArgs(), calleeFunctionDecl.getNumParams()); // if it's an array, passing it by value to a method typically means the // callee takes a pointer and can modify the array if (varDecl->getType()->isConstantArrayType())
{ for (unsigned i = 0; i < len; ++i) if (callExpr.getArg(i) == child) if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i)).Pointer().NonConst()) returntrue;
} else
{ for (unsigned i = 0; i < len; ++i) if (callExpr.getArg(i) == child) if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i))
.LvalueReference()
.NonConst()) returntrue;
} returnfalse;
}
// Extract the functionprototype from a type
clang::Type const* calleeType = callExpr->getCallee()->getType().getTypePtr(); if (auto pointerType = calleeType->getUnqualifiedDesugaredType()->getAs<clang::PointerType>())
{ if (auto prototype = pointerType->getPointeeType()
->getUnqualifiedDesugaredType()
->getAs<FunctionProtoType>())
{ return CalleeWrapper(prototype);
}
}
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.