in src/checks/level1/qdeleteall.cpp [29:78]
void QDeleteAll::VisitStmt(clang::Stmt *stmt)
{
// Find a call to QMap/QSet/QHash::values/keys
auto *offendingCall = dyn_cast<CXXMemberCallExpr>(stmt);
FunctionDecl *func = offendingCall ? offendingCall->getDirectCallee() : nullptr;
if (!func) {
return;
}
const std::string funcName = func->getNameAsString();
const bool isValues = funcName == "values";
if (!isValues && funcName != "keys") {
return;
}
std::string offendingClassName;
// In case QMultiHash::values is used in Qt5, values is defined in the QHash baseclass, look up the original record to be sure we have a QMultiHash
if (auto *cast = dyn_cast<ImplicitCastExpr>(offendingCall->getImplicitObjectArgument())) {
if (auto *subExpr = dyn_cast<DeclRefExpr>(cast->getSubExpr())) {
if (auto *ptr = subExpr->getType().getTypePtrOrNull(); ptr && ptr->isRecordType()) {
offendingClassName = ptr->getAsRecordDecl()->getNameAsString();
}
}
}
// Check if we have whitelisted the classname
if (offendingClassName.empty() || !clazy::isQtAssociativeContainer(offendingClassName)) {
return;
}
// Once found see if the first parent call is qDeleteAll
int i = 1;
Stmt *p = clazy::parent(m_context->parentMap, stmt, i);
while (p) {
auto *pc = dyn_cast<CallExpr>(p);
if (const FunctionDecl *f = pc ? pc->getDirectCallee() : nullptr) {
if ((clazy::name(f) == "qDeleteAll")
&& (func->getNumParams() == 0)) { // Ignore values method calls where lookup-parameter is given, like the deprecated QHash::values(Key)
std::string msg = "qDeleteAll() is being used on an unnecessary temporary container created by " + offendingClassName + "::" + funcName + "()";
if (isValues) {
msg += ", use qDeleteAll(mycontainer) instead";
} else {
msg += ", use qDeleteAll(mycontainer.keyBegin(), mycontainer.keyEnd()) instead";
}
emitWarning(p->getBeginLoc(), msg);
}
break;
}
++i;
p = clazy::parent(m_context->parentMap, stmt, i);
}
}