void Qt6DeprecatedAPIFixes::fixForDeprecatedOperator()

in src/checks/manuallevel/qt6-deprecated-api-fixes.cpp [413:508]


void Qt6DeprecatedAPIFixes::fixForDeprecatedOperator(Stmt *stmt, const std::string &className)
{
    // only interested in '=' operator for QDir
    std::vector<FixItHint> fixits;
    std::string message;
    std::string replacement;
    SourceLocation warningLocation;
    SourceRange fixitRange;
    Stmt *child = clazy::childAt(stmt, 0);
    bool foundOperator = false;
    DeclRefExpr *decl = nullptr;
    while (child) {
        decl = dyn_cast<DeclRefExpr>(child);
        if (!decl) {
            child = clazy::childAt(child, 0);
            continue;
        }

        if (className == "QDir") {
            foundOperator = foundQDirDeprecatedOperator(decl, lo());
        } else if (className == "QVariant") {
            foundOperator = foundQVariantDeprecatedOperator(decl);
        }

        if (foundOperator) {
            warningLocation = decl->getLocation();
            break;
        }
        child = clazy::childAt(child, 0);
    }

    if (!foundOperator) {
        return;
    }

    // Getting the two arguments of the operator to build the replacement
    auto *oppCallExpr = dyn_cast<CXXOperatorCallExpr>(stmt);
    auto *arg0Size = oppCallExpr->getArg(0);
    auto *arg1Size = oppCallExpr->getArg(1);
    auto charRange = Lexer::getAsCharRange(arg0Size->getSourceRange(), m_sm, lo());
    auto replacementVar1 = Lexer::getSourceText(charRange, m_sm, lo());
    charRange = Lexer::getAsCharRange(arg1Size->getSourceRange(), m_sm, lo());
    auto replacementVar2 = Lexer::getSourceText(charRange, m_sm, lo());

    replacementVar1 = replacementVar1.rtrim(' ');
    replacementVar2 = replacementVar2.ltrim(' ');

    if (className == "QDir") {
        message = " function setPath() has to be used in Qt6";
        // Get the quality type of the operator first argument.
        // qdir_var1 = var2 => qdir_var1->setPath(var2) or qdir_var1.setPath(var2)
        // the qdir_var1 correspond to second child of the QDir operator
        child = clazy::childAt(stmt, 1);
        bool isPointer = false;
        while (child) {
            auto *castExpr = dyn_cast<ImplicitCastExpr>(child);
            auto *parent = dyn_cast<ParenExpr>(child);
            if (castExpr || parent) {
                child = clazy::childAt(child, 0);
                continue;
            }
            auto *uni = dyn_cast<UnaryOperator>(child);
            if (uni) {
#if LLVM_VERSION_MAJOR >= 19
#define STRING_EQUALS(a, b) a == b
#else
#define STRING_EQUALS(a, b) a.equals(b)
#endif
                if (STRING_EQUALS(clang::UnaryOperator::getOpcodeStr(uni->getOpcode()), "*")) {
                    isPointer = true;
                }
            }
            break;
        }
        if (isPointer) {
            while (replacementVar1.consume_front("(")) {
                replacementVar1.consume_back(")");
            }
            replacementVar1.consume_front("*");
        }
        replacement = buildReplacementforQDir(decl, isPointer, replacementVar1.str(), replacementVar2.str());
    } else if (className == "QVariant") {
        message = " operator does not exist in Qt6. Using QVariant::compare() instead.";
        replacement = buildReplacementForQVariant(decl, replacementVar1.str(), replacementVar2.str());
    }

    // If a macro is present in the stmt range the spelling location is used
    // This is producing a wrong fix. So we're forcing the use of expansion location
    FullSourceLoc endLoc(stmt->getEndLoc(), m_sm);
    SourceRange range(stmt->getBeginLoc(), endLoc.getExpansionLoc());
    fixitRange = range;
    fixits.push_back(FixItHint::CreateReplacement(fixitRange, replacement));
    emitWarning(warningLocation, message, fixits);

    return;
}