void Foreach::VisitStmt()

in src/checks/level1/foreach.cpp [41:121]


void Foreach::VisitStmt(clang::Stmt *stmt)
{
    const PreProcessorVisitor *preProcessorVisitor = m_context->preprocessorVisitor;
    if (!preProcessorVisitor || preProcessorVisitor->qtVersion() >= 50900) {
        // Disabled since 5.9 because the Q_FOREACH internals changed.
        // Not worth fixing it because range-loop is recommended
        return;
    }

    auto *forStm = dyn_cast<ForStmt>(stmt);
    if (forStm) {
        m_lastForStmt = forStm;
        return;
    }

    if (!m_lastForStmt) {
        return;
    }

    auto *constructExpr = dyn_cast<CXXConstructExpr>(stmt);
    if (!constructExpr || constructExpr->getNumArgs() < 1) {
        return;
    }

    const CXXConstructorDecl *constructorDecl = constructExpr->getConstructor();
    if (!constructorDecl || clazy::name(constructorDecl) != "QForeachContainer") {
        return;
    }

    std::vector<DeclRefExpr *> declRefExprs;
    clazy::getChilds<DeclRefExpr>(constructExpr, declRefExprs);
    if (declRefExprs.empty()) {
        return;
    }

    // Get the container value declaration
    DeclRefExpr *declRefExpr = declRefExprs.front();
    auto *valueDecl = dyn_cast<ValueDecl>(declRefExpr->getDecl());
    if (!valueDecl) {
        return;
    }

    QualType containerQualType = constructExpr->getArg(0)->getType();
    const Type *containerType = containerQualType.getTypePtrOrNull();
    CXXRecordDecl *const containerRecord = containerType ? containerType->getAsCXXRecordDecl() : nullptr;

    if (!containerRecord) {
        return;
    }

    auto *rootBaseClass = Utils::rootBaseClass(containerRecord);
    StringRef containerClassName = clazy::name(rootBaseClass);
    const bool isQtContainer = clazy::isQtIterableClass(containerClassName);
    if (containerClassName.empty()) {
        emitWarning(stmt->getBeginLoc(), "internal error, couldn't get class name of foreach container, please report a bug");
        return;
    }
    if (!isQtContainer) {
        emitWarning(stmt->getBeginLoc(), "foreach with STL container causes deep-copy (" + rootBaseClass->getQualifiedNameAsString() + ')');
        return;
    } else if (containerClassName == "QVarLengthArray") {
        emitWarning(stmt->getBeginLoc(), "foreach with QVarLengthArray causes deep-copy");
        return;
    }

    checkBigTypeMissingRef();

    if (isa<MaterializeTemporaryExpr>(constructExpr->getArg(0))) { // Nothing else to check
        return;
    }

    // const containers are fine
    if (valueDecl->getType().isConstQualified()) {
        return;
    }

    // Now look inside the for statement for detachments
    if (containsDetachments(m_lastForStmt, valueDecl)) {
        emitWarning(stmt->getBeginLoc(), "foreach container detached");
    }
}