in src/checks/level0/strict-iterators.cpp [63:153]
bool StrictIterators::handleImplicitCast(ImplicitCastExpr *implicitCast)
{
if (!implicitCast) {
return false;
}
const std::string nameTo = clazy::simpleTypeName(implicitCast->getType(), m_context->ci.getLangOpts());
const QualType typeTo = implicitCast->getType();
CXXRecordDecl *recordTo = clazy::parentRecordForTypedef(typeTo);
if (recordTo && !clazy::isQtCOWIterableClass(recordTo)) {
return false;
}
recordTo = clazy::typeAsRecord(typeTo);
if (recordTo && !clazy::isQtCOWIterator(recordTo)) {
return false;
}
assert(implicitCast->getSubExpr());
if (isMemberVariable(implicitCast->getSubExpr())) {
// Comparing a const_iterator against a member QVector<T>::iterator won't detach the container
return false;
}
QualType typeFrom = implicitCast->getSubExpr()->getType();
CXXRecordDecl *recordFrom = clazy::parentRecordForTypedef(typeFrom);
if (recordFrom && !clazy::isQtCOWIterableClass(recordFrom)) {
return false;
}
// const_iterator might be a typedef to pointer, like const T *, instead of a class, so just check for const qualification in that case
if (!(clazy::pointeeQualType(typeTo).isConstQualified() || clazy::endsWith(nameTo, "const_iterator"))) {
return false;
}
// Allow conversions for mutating member functions of Qt container classes
if (implicitCast->getCastKind() == CK_ConstructorConversion) {
if (auto *memberCall = dyn_cast_or_null<CXXMemberCallExpr>(m_context->parentMap->getParent(implicitCast))) {
auto memberFunctionDecl = memberCall->getMethodDecl();
if (auto *parentClass = memberFunctionDecl->getParent()) {
static const std::vector<std::string> allow = {
"QMap<>::insert",
"QMap<>::erase",
"QHash<>::erase",
"QMultiHash<>::erase",
"QList<>::emplace",
"QList<>::erase",
"QList<>::insert",
"QVarLengthArray<>::emplace",
"QVarLengthArray<>::erase",
"QVarLengthArray<>::insert",
"QSet<>::erase",
"QSet<>::insert",
"QMultiMap<>::erase",
"QMultiMap<>::insert",
};
const auto qualifiedName = parentClass->getNameAsString() + "<>::" + memberFunctionDecl->getNameAsString();
if (clazy::contains(allow, qualifiedName)) {
return false;
}
}
}
emitWarning(implicitCast, "Mixing iterators with const_iterators");
return true;
}
// TODO: some util function to get the name of a nested class
const bool nameToIsIterator = nameTo == "iterator" || clazy::endsWith(nameTo, "::iterator");
if (nameToIsIterator) {
return false;
}
const std::string nameFrom = clazy::simpleTypeName(typeFrom, m_context->ci.getLangOpts());
const bool nameFromIsIterator = nameFrom == "iterator" || clazy::endsWith(nameFrom, "::iterator");
if (!nameFromIsIterator) {
return false;
}
auto *p = m_context->parentMap->getParent(implicitCast);
if (isa<CXXOperatorCallExpr>(p)) {
return false;
}
emitWarning(implicitCast, "Mixing iterators with const_iterators");
return true;
}