void QStringAllocations::VisitCtor()

in src/checks/level2/qstring-allocations.cpp [210:336]


void QStringAllocations::VisitCtor(CXXConstructExpr *ctorExpr)
{
    CXXConstructorDecl *ctorDecl = ctorExpr->getConstructor();
    if (!clazy::isOfClass(ctorDecl, "QString")) {
        return;
    }

    if (Utils::insideCTORCall(m_context->parentMap, ctorExpr, {"QRegExp", "QIcon"})) {
        // https://blogs.kde.org/2015/11/05/qregexp-qstringliteral-crash-exit
        return;
    }

    if (!isOptionSet("no-msvc-compat")) {
        auto *initializerList = clazy::getFirstParentOfType<InitListExpr>(m_context->parentMap, ctorExpr);
        if (initializerList != nullptr) {
            return; // Nothing to do here, MSVC doesn't like it
        }

        StringLiteral *lt = stringLiteralForCall(ctorExpr);
        if (lt && lt->getNumConcatenated() > 1) {
            return; // Nothing to do here, MSVC doesn't like it
        }
    }

    bool isQLatin1String = false;
    std::string paramType;
    if (hasCharPtrArgument(ctorDecl, 1)) {
        paramType = "const char*";
    } else if (ctorDecl->param_size() == 1
               && (clazy::hasArgumentOfType(ctorDecl, "QLatin1String", lo()) || clazy::hasArgumentOfType(ctorDecl, "QLatin1StringView", lo()))) {
        paramType = "QLatin1String";
        isQLatin1String = true;
    } else {
        return;
    }

    std::string msg = std::string("QString(") + paramType + std::string(") being called");

    if (isQLatin1String) {
        ConditionalOperator *ternary = nullptr;
        Latin1Expr qlatin1expr = qlatin1CtorExpr(ctorExpr, ternary);
        if (!qlatin1expr.isValid()) {
            return;
        }

        auto *qlatin1Ctor = qlatin1expr.qlatin1ctorexpr;

        if (qlatin1Ctor->getBeginLoc().isMacroID()) {
            auto macroName = Lexer::getImmediateMacroName(qlatin1Ctor->getBeginLoc(), sm(), lo());
            if (macroName == "Q_GLOBAL_STATIC_WITH_ARGS") { // bug #391807
                return;
            }
        }

        std::vector<FixItHint> fixits;
        if (qlatin1expr.enableFixit) {
            if (!qlatin1Ctor->getBeginLoc().isMacroID()) {
                if (!ternary) {
                    fixits = fixItReplaceWordWithWord(qlatin1Ctor, "QStringLiteral", "QLatin1String");
                    bool shouldRemoveQString = qlatin1Ctor->getBeginLoc().getRawEncoding() != ctorExpr->getBeginLoc().getRawEncoding()
                        && dyn_cast_or_null<CXXBindTemporaryExpr>(clazy::parent(m_context->parentMap, ctorExpr));
                    if (shouldRemoveQString) {
                        // This is the case of QString(QLatin1String("foo")), which we just fixed to be QString(QStringLiteral("foo")), so now remove QString
                        auto removalFixits = clazy::fixItRemoveToken(&m_astContext, ctorExpr, true);
                        if (removalFixits.empty()) {
                            queueManualFixitWarning(ctorExpr->getBeginLoc(), "Internal error: invalid start or end location");
                        } else {
                            clazy::append(removalFixits, fixits);
                        }
                    }
                } else {
                    fixits = fixItReplaceWordWithWordInTernary(ternary);
                }
            } else {
                queueManualFixitWarning(qlatin1Ctor->getBeginLoc(), "Can't use QStringLiteral in macro");
            }
        }

        maybeEmitWarning(ctorExpr->getBeginLoc(), msg, fixits);
    } else {
        std::vector<FixItHint> fixits;
        if (clazy::hasChildren(ctorExpr)) {
            auto *pointerDecay = dyn_cast<ImplicitCastExpr>(*(ctorExpr->child_begin()));
            if (clazy::hasChildren(pointerDecay)) {
                auto *lt = dyn_cast<StringLiteral>(*pointerDecay->child_begin());
                if (lt) {
                    Stmt *grandParent = clazy::parent(m_context->parentMap, lt, 2);
                    Stmt *grandGrandParent = clazy::parent(m_context->parentMap, lt, 3);
                    Stmt *grandGrandGrandParent = clazy::parent(m_context->parentMap, lt, 4);
                    if (grandParent == ctorExpr && grandGrandParent && isa<CXXBindTemporaryExpr>(grandGrandParent) && grandGrandGrandParent
                        && isa<CXXFunctionalCastExpr>(grandGrandGrandParent)) {
                        // This is the case of QString("foo"), replace QString

                        const bool literalIsEmpty = lt->getLength() == 0;
                        if (literalIsEmpty && clazy::getFirstParentOfType<MemberExpr>(m_context->parentMap, ctorExpr) == nullptr) {
                            fixits = fixItReplaceWordWithWord(ctorExpr, "QLatin1String", "QString");
                        } else if (!ctorExpr->getBeginLoc().isMacroID()) {
                            fixits = fixItReplaceWordWithWord(ctorExpr, "QStringLiteral", "QString");
                        } else {
                            queueManualFixitWarning(ctorExpr->getBeginLoc(), "Can't use QStringLiteral in macro.");
                        }
                    } else {
                        auto *parentMemberCallExpr =
                            clazy::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap,
                                                                           lt,
                                                                           /*maxDepth=*/6); // 6 seems like a nice max from the ASTs I've seen

                        std::string replacement = "QStringLiteral";
                        if (parentMemberCallExpr) {
                            FunctionDecl *fDecl = parentMemberCallExpr->getDirectCallee();
                            if (fDecl) {
                                auto *method = dyn_cast<CXXMethodDecl>(fDecl);
                                if (method && betterTakeQLatin1String(method, lt)) {
                                    replacement = "QLatin1String";
                                }
                            }
                        }

                        fixits = fixItRawLiteral(lt, replacement, nullptr);
                    }
                }
            }
        }

        maybeEmitWarning(ctorExpr->getBeginLoc(), msg, fixits);
    }
}