/* -*- 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. *
*/
namespace loplugin
{ /* This is a compile check. The results of this plugin need to be checked by hand, since it is a collection of heuristics.
Check for unused variable where (*) we never call methods that return information from the variable. (*) we never pass the variable to anything else
Classes which are safe to be warned about need to be marked using SAL_WARN_UNUSED (see e.g. OUString). For external classes such as std::vector that cannot be edited there is a manual list.
This is an expensive plugin, since it walks up the parent tree, so it is off by default.
*/
std::unordered_set<VarDecl const*> interestingSet; // used to dump the last place that said the usage was unused, for debug purposes
std::unordered_map<VarDecl const*, Stmt const*> interestingDebugMap;
};
// ignore QA folders if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/qa/")) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/i18npool/qa/")) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/qa/")) return;
// vector of shared_ptr used to delay destruction if (fn == SRCDIR "/cppuhelper/source/servicemanager.cxx") return; if (fn == SRCDIR "/i18nlangtag/source/languagetag/languagetag.cxx") return; // unordered_set of Reference to delay destruction if (fn == SRCDIR "/stoc/source/servicemanager/servicemanager.cxx") return; // TODO "operator >>" fooling me here if (fn == SRCDIR "/editeng/source/accessibility/AccessibleEditableTextPara.cxx") return; // some weird stuff if (fn == SRCDIR "/sfx2/source/dialog/dinfdlg.cxx") return; // std::vector< Reference< XInterface > > keep alive if (fn == SRCDIR "/dbaccess/source/core/dataaccess/databasedocument.cxx") return; // template magic if (fn == SRCDIR "/sc/source/core/tool/scmatrix.cxx") return; // storing local copy of Link<> if (fn == SRCDIR "/sc/source/ui/miscdlgs/simpref.cxx") return; // Using an SwPaM to do stuff if (fn == SRCDIR "/sw/source/core/crsr/bookmark.cxx") return; // index variable in for loop? if (fn == SRCDIR "/sw/source/uibase/docvw/edtwin.cxx") return; // TODO "operator >>" fooling me here if (fn == SRCDIR "/sw/source/filter/ww8/ww8par.cxx") return; // TODO "operator >>" fooling me here if (fn == SRCDIR "/sc/source/filter/excel/xistream.cxx") return;
bool isWarnUnusedType(QualType type)
{ if (autoconst t = type->getAs<TypedefType>())
{ if (t->getDecl()->hasAttr<WarnUnusedAttr>())
{ returntrue;
}
} if (autoconst t = type->getAs<RecordType>())
{ if (t->getDecl()->hasAttr<WarnUnusedAttr>())
{ returntrue;
}
} return loplugin::isExtraWarnUnusedType(type);
}
bool UnusedVariableMore::VisitVarDecl(VarDecl const* var)
{ if (ignoreLocation(var)) returntrue; if (var->isDefinedOutsideFunctionOrMethod()) returntrue; if (isa<ParmVarDecl>(var)) returntrue; if (!isWarnUnusedType(var->getType())) returntrue;
// some false + auto dc = loplugin::TypeCheck(var->getType()); if (dc.Class("ZCodec").GlobalNamespace()) returntrue; if (dc.Class("ScopedVclPtrInstance").GlobalNamespace()) returntrue; if (dc.Class("VclPtrInstance").GlobalNamespace()) returntrue; if (dc.Class("Config").GlobalNamespace()) returntrue; // I think these classes modify global state somehow if (dc.Class("SvtHistoryOptions").GlobalNamespace()) returntrue; if (dc.Class("SvtSecurityOptions").GlobalNamespace()) returntrue; if (dc.Class("SvtViewOptions").GlobalNamespace()) returntrue; if (dc.Class("SvtUserOptions").GlobalNamespace()) returntrue; if (dc.Class("SvtMenuOptions").GlobalNamespace()) returntrue; if (dc.Class("SvtPathOptions").GlobalNamespace()) returntrue; if (dc.Class("SvtSysLocaleOptions").GlobalNamespace()) returntrue;
interestingSet.insert(var); returntrue;
}
bool UnusedVariableMore::VisitDeclRefExpr(DeclRefExpr const* declRefExpr)
{ if (ignoreLocation(declRefExpr)) returntrue; auto varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()); if (!varDecl) returntrue; if (interestingSet.find(varDecl) == interestingSet.end()) returntrue; if (!checkifUnused(declRefExpr, varDecl))
interestingSet.erase(varDecl); returntrue;
}
// Walk up from a statement that contains a DeclRefExpr, checking if the usage means that the // related VarDecl is unused. bool UnusedVariableMore::checkifUnused(Stmt const* stmt, VarDecl const* varDecl)
{ const Stmt* parent = getParentStmt(stmt); if (!parent)
{ // check if we're inside a CXXCtorInitializer auto parentsRange = compiler.getASTContext().getParents(*stmt); if (parentsRange.begin() != parentsRange.end())
{ auto parentDecl = parentsRange.begin()->get<Decl>(); if (parentDecl && (isa<CXXConstructorDecl>(parentDecl) || isa<VarDecl>(parentDecl))) returnfalse;
}
interestingDebugMap[varDecl] = stmt; returntrue;
}
if (isa<ReturnStmt>(parent)) returnfalse; if (isa<IfStmt>(parent)) returnfalse; if (isa<SwitchStmt>(parent)) returnfalse; if (isa<InitListExpr>(parent)) returnfalse; if (isa<CXXConstructExpr>(parent)) returnfalse; if (isa<BinaryOperator>(parent)) returnfalse; if (isa<UnaryOperator>(parent)) returnfalse; if (isa<ConditionalOperator>(parent)) returnfalse; if (isa<ArraySubscriptExpr>(parent)) returnfalse; if (isa<CXXBindTemporaryExpr>(parent)) return checkifUnused(parent, varDecl); if (isa<MaterializeTemporaryExpr>(parent)) return checkifUnused(parent, varDecl);
if (isa<CompoundStmt>(parent))
{
interestingDebugMap[varDecl] = parent; returntrue;
}
// check for cast to void if (auto explicitCastExpr = dyn_cast<ExplicitCastExpr>(parent))
{ if (loplugin::TypeCheck(explicitCastExpr->getTypeAsWritten()).Void()) returnfalse;
}
if (isa<MemberExpr>(parent)) return checkifUnused(parent, varDecl); if (isa<ExprWithCleanups>(parent)) return checkifUnused(parent, varDecl); if (isa<CastExpr>(parent)) return checkifUnused(parent, varDecl); if (isa<ParenExpr>(parent)) return checkifUnused(parent, varDecl);
if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent))
{ const CXXMethodDecl* calleeMethodDecl
= dyn_cast_or_null<CXXMethodDecl>(operatorCallExpr->getDirectCallee()); if (calleeMethodDecl)
{ if (calleeMethodDecl->getNumParams() == 0) return checkifUnused(parent, varDecl); if (operatorCallExpr->getArg(0) == stmt) return checkifUnused(parent, varDecl);
}
} if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(parent))
{ const MemberExpr* memberExpr = dyn_cast<MemberExpr>(stmt); if (memberExpr && memberCallExpr->getImplicitObjectArgument() == memberExpr->getBase())
{ // if we are calling a method on the varDecl, walk up if (!checkifUnused(parent, varDecl)) returnfalse; // check if we are passing something to the var by non-const ref, in which case it is updating something else for us const FunctionDecl* calleeFunctionDecl = memberCallExpr->getDirectCallee(); if (calleeFunctionDecl)
{ if (calleeFunctionDecl->isVariadic()) returnfalse; for (unsigned i = 0; i < memberCallExpr->getNumArgs(); ++i)
{ if (i >= calleeFunctionDecl->getNumParams()) // can happen in template code
{
interestingDebugMap[varDecl] = parent; returntrue;
} if (!isOkForParameter(calleeFunctionDecl->getParamDecl(i)->getType())) returnfalse;
}
}
interestingDebugMap[varDecl] = parent; returntrue;
}
} if (isa<CallExpr>(parent)) returnfalse;
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 ist noch experimentell.