File indexing completed on 2024-05-12 05:41:02

0001 /*
0002     SPDX-FileCopyrightText: 2020 The Qt Company Ltd.
0003     SPDX-FileCopyrightText: 2020 Lucie Gerard <lucie.gerard@qt.io>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "qt6-deprecated-api-fixes.h"
0009 #include "ClazyContext.h"
0010 #include "ContextUtils.h"
0011 #include "FixItUtils.h"
0012 #include "HierarchyUtils.h"
0013 #include "StringUtils.h"
0014 #include "Utils.h"
0015 #include "clazy_stl.h"
0016 
0017 #include <clang/AST/Decl.h>
0018 #include <clang/AST/DeclCXX.h>
0019 #include <clang/AST/Expr.h>
0020 #include <clang/AST/ExprCXX.h>
0021 #include <clang/AST/Stmt.h>
0022 #include <clang/AST/Type.h>
0023 #include <clang/Basic/Diagnostic.h>
0024 #include <clang/Basic/LLVM.h>
0025 #include <clang/Basic/SourceLocation.h>
0026 #include <clang/Lex/Lexer.h>
0027 #include <llvm/ADT/ArrayRef.h>
0028 #include <llvm/ADT/StringRef.h>
0029 #include <llvm/Support/Casting.h>
0030 
0031 using namespace clang;
0032 
0033 Qt6DeprecatedAPIFixes::Qt6DeprecatedAPIFixes(const std::string &name, ClazyContext *context)
0034     : CheckBase(name, context, Option_CanIgnoreIncludes)
0035 {
0036     enablePreProcessorCallbacks();
0037 }
0038 
0039 void replacementForQWizard(const std::string &functionName, std::string &message, std::string &replacement)
0040 {
0041     message = "call function QProcess::";
0042     message += functionName;
0043     message += "(). Use function QProcess::visitedIds() instead";
0044 
0045     replacement = "visitedIds";
0046 }
0047 
0048 bool replacementForQDate(clang::Stmt *parent,
0049                          std::string &message,
0050                          std::string &replacement,
0051                          SourceLocation &warningLocation,
0052                          SourceRange &fixitRange,
0053                          LangOptions lo)
0054 {
0055     // The one with two arguments: Qt::DateFormat format, QCalendar cal
0056     auto *callExp = dyn_cast<CXXMemberCallExpr>(parent);
0057     if (!callExp) {
0058         return false;
0059     }
0060     auto *func = callExp->getDirectCallee();
0061     if (!func) {
0062         return false;
0063     }
0064     int i = 1;
0065     if (func->getNumParams() != 2) {
0066         return false;
0067     }
0068     for (auto *it = func->param_begin(); it != func->param_end(); it++) {
0069         ParmVarDecl *param = *it;
0070         if (i == 1 && param->getType().getAsString(lo) != "Qt::DateFormat") {
0071             return false;
0072         }
0073         if (i == 2 && param->getType().getAsString(lo) != "QCalendar") {
0074             return false;
0075         }
0076         i++;
0077     }
0078     Stmt *firstArg = clazy::childAt(parent, 1);
0079     Stmt *secondArg = clazy::childAt(parent, 2);
0080     auto *declFirstArg = dyn_cast<DeclRefExpr>(firstArg);
0081     if (!firstArg || !secondArg || !declFirstArg) {
0082         return false;
0083     }
0084     fixitRange = SourceRange(firstArg->getEndLoc(), secondArg->getEndLoc());
0085     message = "replacing with function omitting the calendar. Change manually and use QLocale if you want to keep the calendar.";
0086     warningLocation = secondArg->getBeginLoc();
0087     replacement = declFirstArg->getNameInfo().getAsString();
0088     return true;
0089 }
0090 
0091 static std::set<std::string> qButtonGroupDeprecatedFunctions = {"buttonClicked", "buttonPressed", "buttonReleased", "buttonToggled"};
0092 
0093 bool replacementForQButtonGroup(clang::MemberExpr *membExpr, std::string &message, std::string &replacement)
0094 {
0095     auto *declfunc = membExpr->getReferencedDeclOfCallee()->getAsFunction();
0096     std::string paramType;
0097     for (auto *param : Utils::functionParameters(declfunc)) {
0098         paramType = param->getType().getAsString();
0099         break;
0100     }
0101     // only the function with "int" as first argument are deprecated
0102     if (paramType != "int") {
0103         return false;
0104     }
0105 
0106     std::string functionName = membExpr->getMemberNameInfo().getAsString();
0107     std::string newFunctionName = "id";
0108     newFunctionName += functionName.substr(6, 8);
0109 
0110     message = "call function QButtonGroup::";
0111     message += functionName;
0112     message += "(int";
0113     if (declfunc->param_size() > 1) {
0114         message += ", bool";
0115     }
0116     message += "). Use function QButtonGroup";
0117     message += newFunctionName;
0118     message += " instead.";
0119 
0120     replacement = newFunctionName;
0121     return true;
0122 }
0123 
0124 inline bool isFirstArgQStringConstRef(FunctionDecl *declfunc, LangOptions lo)
0125 {
0126     auto params = Utils::functionParameters(declfunc);
0127     return !params.empty() && params.front()->getType().getAsString(lo) == "const QString &";
0128 }
0129 
0130 bool warningForQTextBrowser(clang::MemberExpr *membExpr, std::string &message, LangOptions lo)
0131 {
0132     auto *declfunc = membExpr->getReferencedDeclOfCallee()->getAsFunction();
0133     if (isFirstArgQStringConstRef(declfunc, lo)) {
0134         message = "Using QTextBrowser::highlighted(const QString &). Use QTextBrowser::highlighted(const QUrl &) instead.";
0135         return true;
0136     }
0137     return false;
0138 }
0139 
0140 bool warningForQComboBox(clang::MemberExpr *membExpr, std::string &message, LangOptions lo)
0141 {
0142     auto *declfunc = membExpr->getReferencedDeclOfCallee()->getAsFunction();
0143     if (isFirstArgQStringConstRef(declfunc, lo)) {
0144         message = "Use currentIndexChanged(int) instead, and get the text using itemText(index).";
0145         return true;
0146     }
0147     return false;
0148 }
0149 
0150 bool replacementForQComboBox(clang::MemberExpr *membExpr, const std::string &functionName, std::string &message, std::string &replacement, LangOptions lo)
0151 {
0152     auto *declfunc = membExpr->getReferencedDeclOfCallee()->getAsFunction();
0153     if (!isFirstArgQStringConstRef(declfunc, lo)) {
0154         return false;
0155     }
0156 
0157     if (functionName == "activated") {
0158         message = "Using QComboBox::activated(const QString &). Use textActiated() instead";
0159         replacement = "textActivated";
0160     } else if (functionName == "highlighted") {
0161         message = "Using QComboBox::hilighted(const QString &). Use textHighlighted() instead";
0162         replacement = "textHighlighted";
0163     } else {
0164         return false;
0165     }
0166     return true;
0167 }
0168 
0169 static std::set<std::string> qProcessDeprecatedFunctions = {"start"};
0170 
0171 void replacementForQProcess(const std::string &functionName, std::string &message, std::string &replacement)
0172 {
0173     message = "call function QProcess::";
0174     message += functionName;
0175     message += "(). Use function QProcess::";
0176     message += functionName;
0177     message += "Command() instead";
0178 
0179     replacement = functionName;
0180     replacement += "Command";
0181 }
0182 
0183 void replacementForQSignalMapper(clang::MemberExpr *membExpr, std::string &message, std::string &replacement, clang::LangOptions lo)
0184 {
0185     auto *declfunc = membExpr->getReferencedDeclOfCallee()->getAsFunction();
0186     std::string paramType;
0187     for (auto *param : Utils::functionParameters(declfunc)) {
0188         paramType = param->getType().getAsString(lo);
0189     }
0190 
0191     std::string functionNameExtention;
0192     if (paramType == "int") {
0193         functionNameExtention = "Int";
0194     } else if (paramType == "const QString &") {
0195         functionNameExtention = "String";
0196     } else if (paramType == "QWidget *") {
0197         functionNameExtention = "Object";
0198     } else if (paramType == "QObject *") {
0199         functionNameExtention = "Object";
0200     }
0201 
0202     message = "call function QSignalMapper::mapped(";
0203     message += paramType;
0204     message += "). Use function QSignalMapper::mapped";
0205     message += functionNameExtention;
0206     message += "(";
0207     message += paramType;
0208     message += ") instead.";
0209 
0210     replacement = "mapped";
0211     replacement += functionNameExtention;
0212 }
0213 
0214 void replacementForQResource(const std::string & /*functionName*/, std::string &message, std::string &replacement)
0215 {
0216     message = "call function QRessource::isCompressed(). Use function QProcess::compressionAlgorithm() instead.";
0217     replacement = "compressionAlgorithm";
0218 }
0219 
0220 static std::set<std::string> qSetDeprecatedOperators = {"operator--", "operator+", "operator-", "operator+=", "operator-="};
0221 static std::set<std::string> qSetDeprecatedFunctions = {"rbegin", "rend", "crbegin", "crend", "hasPrevious", "previous", "peekPrevious", "findPrevious"};
0222 static std::set<std::string> qHashDeprecatedFunctions = {"hasPrevious", "previous", "peekPrevious", "findPrevious"};
0223 
0224 bool isQSetDepreprecatedOperator(const std::string &functionName, const std::string &contextName, std::string &message)
0225 {
0226     if (qSetDeprecatedOperators.find(functionName) == qSetDeprecatedOperators.end()) {
0227         return false;
0228     }
0229     if ((clazy::startsWith(contextName, "QSet<") || clazy::startsWith(contextName, "QHash<")) && clazy::endsWith(contextName, "iterator")) {
0230         if (clazy::startsWith(contextName, "QSet<")) {
0231             message = "QSet iterator categories changed from bidirectional to forward. Please port your code manually";
0232         } else {
0233             message = "QHash iterator categories changed from bidirectional to forward. Please port your code manually";
0234         }
0235 
0236         return true;
0237     }
0238     return false;
0239 }
0240 
0241 static std::set<std::string> qGraphicsViewFunctions = {"matrix", "setMatrix", "resetMatrix"};
0242 
0243 void warningForGraphicsViews(const std::string &functionName, std::string &message)
0244 {
0245     if (functionName == "matrix") {
0246         message = "Using QGraphicsView::matrix. Use transform() instead";
0247         return;
0248     }
0249     if (functionName == "setMatrix") {
0250         message = "Using QGraphicsView::setMatrix(const QMatrix &). Use setTransform(const QTransform &) instead";
0251         return;
0252     } else if (functionName == "resetMatrix") {
0253         message = "Using QGraphicsView::resetMatrix(). Use resetTransform() instead";
0254         return;
0255     }
0256     return;
0257 }
0258 
0259 static std::set<std::string> qStylePixelMetrix = {"PM_DefaultTopLevelMargin", "PM_DefaultChildMargin", "PM_DefaultLayoutSpacing"};
0260 
0261 static std::set<std::string> qMapFunctions = {"insertMulti", "uniqueKeys", "values", "unite"};
0262 
0263 static std::set<std::string> qTextStreamFunctions = {"bin",           "oct",
0264                                                      "dec",           "hex",
0265                                                      "showbase",      "forcesign",
0266                                                      "forcepoint",    "noshowbase",
0267                                                      "noforcesign",   "noforcepoint",
0268                                                      "uppercasebase", "uppercasedigits",
0269                                                      "lowercasebase", "lowercasedigits",
0270                                                      "fixed",         "scientific",
0271                                                      "left",          "right",
0272                                                      "center",        "endl",
0273                                                      "flush",         "reset",
0274                                                      "bom",           "ws"};
0275 
0276 void replacementForQTextStreamFunctions(const std::string &functionName, std::string &message, std::string &replacement, bool explicitQtNamespace)
0277 {
0278     if (qTextStreamFunctions.find(functionName) == qTextStreamFunctions.end()) {
0279         return;
0280     }
0281     message = "call function QTextStream::";
0282     message += functionName;
0283     message += ". Use function Qt::";
0284     message += functionName;
0285     message += " instead";
0286 
0287     if (!explicitQtNamespace) {
0288         replacement = "Qt::";
0289     }
0290     replacement += functionName;
0291 }
0292 
0293 void replacementForQStringSplitBehavior(const std::string &functionName, std::string &message, std::string &replacement, bool explicitQtNamespace)
0294 {
0295     message = "Use Qt::SplitBehavior variant instead";
0296     if (!explicitQtNamespace) {
0297         replacement = "Qt::";
0298     }
0299     replacement += functionName;
0300 }
0301 
0302 bool getMessageForDeclWarning(const std::string &type, std::string &message)
0303 {
0304     if (clazy::contains(type, "QLinkedList")) {
0305         message = "Using QLinkedList. Use std::list instead";
0306         return true;
0307     }
0308     if (clazy::contains(type, "QMacCocoaViewContainer")) {
0309         message =
0310             "Using QMacCocoaViewContainer."
0311             " Use QWindow::fromWinId and QWidget::createWindowContainer instead";
0312         return true;
0313     } else if (clazy::contains(type, "QMacNativeWidget")) {
0314         message =
0315             "Using QMacNativeWidget."
0316             " Use QWidget::winId instead";
0317         return true;
0318     } else if (clazy::contains(type, "QDirModel")) {
0319         message =
0320             "Using QDirModel."
0321             " Use QFileSystemModel instead";
0322         return true;
0323     } else if (clazy::contains(type, "QString::SplitBehavior")) {
0324         message = "Using QString::SplitBehavior. Use Qt::SplitBehavior variant instead";
0325         return true;
0326     } else {
0327         return false;
0328     }
0329 }
0330 
0331 void Qt6DeprecatedAPIFixes::VisitDecl(clang::Decl *decl)
0332 {
0333     auto *funcDecl = decl->getAsFunction();
0334     auto *varDecl = dyn_cast<VarDecl>(decl);
0335     auto *fieldDecl = dyn_cast<FieldDecl>(decl);
0336 
0337     if (!funcDecl && !varDecl && !fieldDecl) {
0338         return;
0339     }
0340 
0341     DeclaratorDecl *declaratorDecl = nullptr;
0342     QualType qualType;
0343     if (funcDecl) {
0344         declaratorDecl = funcDecl;
0345         qualType = funcDecl->getReturnType();
0346     } else if (varDecl) {
0347         declaratorDecl = varDecl;
0348         qualType = varDecl->getType();
0349     } else if (fieldDecl) {
0350         declaratorDecl = fieldDecl;
0351         qualType = fieldDecl->getType();
0352     }
0353 
0354     std::string message;
0355     if (!getMessageForDeclWarning(qualType.getAsString(), message)) {
0356         return;
0357     }
0358 
0359     std::vector<FixItHint> fixits;
0360 #if LLVM_VERSION_MAJOR >= 10
0361     const std::string type = qualType.getAsString();
0362 
0363     if (clazy::endsWith(type, "QString::SplitBehavior")) {
0364         bool isQtNamespaceExplicit = false;
0365         DeclContext *newcontext = clazy::contextForDecl(m_context->lastDecl);
0366         while (newcontext) {
0367             if (!newcontext) {
0368                 break;
0369             }
0370             if (clang::isa<NamespaceDecl>(newcontext)) {
0371                 auto *namesdecl = dyn_cast<clang::NamespaceDecl>(newcontext);
0372                 if (namesdecl->getNameAsString() == "Qt") {
0373                     isQtNamespaceExplicit = true;
0374                 }
0375             }
0376             newcontext = newcontext->getParent();
0377         }
0378         std::string replacement;
0379         if (!isQtNamespaceExplicit) {
0380             replacement = "Qt::";
0381         }
0382         replacement += "SplitBehavior";
0383         SourceRange sourceRange(declaratorDecl->getTypeSpecStartLoc(), declaratorDecl->getTypeSpecEndLoc());
0384         fixits.push_back(FixItHint::CreateReplacement(sourceRange, replacement));
0385     }
0386 #endif
0387 
0388     emitWarning(decl->getBeginLoc(), message, fixits);
0389     return;
0390 }
0391 
0392 std::string
0393 Qt6DeprecatedAPIFixes::buildReplacementforQDir(DeclRefExpr * /*decl_operator*/, bool isPointer, std::string replacement, const std::string &replacement_var2)
0394 {
0395     if (isPointer) {
0396         replacement += "->";
0397     } else {
0398         replacement += ".";
0399     }
0400     replacement += "setPath(";
0401     replacement += replacement_var2;
0402     replacement += ")";
0403     return replacement;
0404 }
0405 
0406 std::string
0407 Qt6DeprecatedAPIFixes::buildReplacementForQVariant(DeclRefExpr *decl_operator, const std::string &replacement_var1, const std::string &replacement_var2)
0408 {
0409     std::string replacement = "QVariant::compare(";
0410     replacement += replacement_var1;
0411     replacement += ", ";
0412     replacement += replacement_var2;
0413     replacement += ") ";
0414     replacement += decl_operator->getNameInfo().getAsString().substr(8, 2);
0415     replacement += " 0";
0416     return replacement;
0417 }
0418 
0419 bool foundQDirDeprecatedOperator(DeclRefExpr *decl)
0420 {
0421     return decl->getNameInfo().getAsString() == "operator=";
0422 }
0423 
0424 static std::set<std::string> qVariantDeprecatedOperator = {"operator<", "operator<=", "operator>", "operator>="};
0425 
0426 bool foundQVariantDeprecatedOperator(DeclRefExpr *decl)
0427 {
0428     return qVariantDeprecatedOperator.find(decl->getNameInfo().getAsString()) != qVariantDeprecatedOperator.end();
0429 }
0430 
0431 void Qt6DeprecatedAPIFixes::fixForDeprecatedOperator(Stmt *stmt, const std::string &className)
0432 {
0433     // only interested in '=' operator for QDir
0434     std::vector<FixItHint> fixits;
0435     std::string message;
0436     std::string replacement;
0437     SourceLocation warningLocation;
0438     SourceRange fixitRange;
0439     Stmt *child = clazy::childAt(stmt, 0);
0440     bool foundOperator = false;
0441     DeclRefExpr *decl = nullptr;
0442     while (child) {
0443         decl = dyn_cast<DeclRefExpr>(child);
0444         if (!decl) {
0445             child = clazy::childAt(child, 0);
0446             continue;
0447         }
0448 
0449         if (className == "QDir") {
0450             foundOperator = foundQDirDeprecatedOperator(decl);
0451         } else if (className == "QVariant") {
0452             foundOperator = foundQVariantDeprecatedOperator(decl);
0453         }
0454 
0455         if (foundOperator) {
0456             warningLocation = decl->getLocation();
0457             break;
0458         }
0459         child = clazy::childAt(child, 0);
0460     }
0461 
0462     if (!foundOperator) {
0463         return;
0464     }
0465 
0466     // Getting the two arguments of the operator to build the replacement
0467     auto *oppCallExpr = dyn_cast<CXXOperatorCallExpr>(stmt);
0468     auto *arg0Size = oppCallExpr->getArg(0);
0469     auto *arg1Size = oppCallExpr->getArg(1);
0470     auto charRange = Lexer::getAsCharRange(arg0Size->getSourceRange(), m_sm, lo());
0471     auto replacementVar1 = Lexer::getSourceText(charRange, m_sm, lo());
0472     charRange = Lexer::getAsCharRange(arg1Size->getSourceRange(), m_sm, lo());
0473     auto replacementVar2 = Lexer::getSourceText(charRange, m_sm, lo());
0474 
0475     replacementVar1 = replacementVar1.rtrim(' ');
0476     replacementVar2 = replacementVar2.ltrim(' ');
0477 
0478     if (className == "QDir") {
0479         message = " function setPath() has to be used in Qt6";
0480         // Get the quality type of the operator first argument.
0481         // qdir_var1 = var2 => qdir_var1->setPath(var2) or qdir_var1.setPath(var2)
0482         // the qdir_var1 correspond to second child of the QDir operator
0483         child = clazy::childAt(stmt, 1);
0484         bool isPointer = false;
0485         while (child) {
0486             auto *castExpr = dyn_cast<ImplicitCastExpr>(child);
0487             auto *parent = dyn_cast<ParenExpr>(child);
0488             if (castExpr || parent) {
0489                 child = clazy::childAt(child, 0);
0490                 continue;
0491             }
0492             auto *uni = dyn_cast<UnaryOperator>(child);
0493             if (uni) {
0494                 if (clang::UnaryOperator::getOpcodeStr(uni->getOpcode()).equals("*")) {
0495                     isPointer = true;
0496                 }
0497             }
0498             break;
0499         }
0500         if (isPointer) {
0501             while (replacementVar1.consume_front("(")) {
0502                 replacementVar1.consume_back(")");
0503             }
0504             replacementVar1.consume_front("*");
0505         }
0506         replacement = buildReplacementforQDir(decl, isPointer, replacementVar1.str(), replacementVar2.str());
0507     } else if (className == "QVariant") {
0508         message = " operator does not exist in Qt6. Using QVariant::compare() instead.";
0509         replacement = buildReplacementForQVariant(decl, replacementVar1.str(), replacementVar2.str());
0510     }
0511 
0512     // If a macro is present in the stmt range the spelling location is used
0513     // This is producing a wrong fix. So we're forcing the use of expansion location
0514     FullSourceLoc endLoc(stmt->getEndLoc(), m_sm);
0515     SourceRange range(stmt->getBeginLoc(), endLoc.getExpansionLoc());
0516     fixitRange = range;
0517     fixits.push_back(FixItHint::CreateReplacement(fixitRange, replacement));
0518     emitWarning(warningLocation, message, fixits);
0519 
0520     return;
0521 }
0522 
0523 void Qt6DeprecatedAPIFixes::VisitStmt(clang::Stmt *stmt)
0524 {
0525     auto *oppCallExpr = dyn_cast<CXXOperatorCallExpr>(stmt);
0526     auto *declRefExp = dyn_cast<DeclRefExpr>(stmt);
0527     auto *membExpr = dyn_cast<MemberExpr>(stmt);
0528     auto *consExpr = dyn_cast<CXXConstructExpr>(stmt);
0529 
0530     SourceLocation warningLocation;
0531     std::string replacement;
0532     std::string message;
0533     SourceRange fixitRange;
0534 
0535     std::vector<FixItHint> fixits;
0536 
0537     if (consExpr) {
0538         auto *constructor = consExpr->getConstructor();
0539         if (!constructor) {
0540             return;
0541         }
0542         if (constructor->getDeclName().getAsString() == "QSplashScreen") {
0543             if (consExpr->getNumArgs() == 0) {
0544                 return;
0545             }
0546             if (consExpr->getArg(0)->getType().getAsString(lo()) != "QWidget *") {
0547                 return;
0548             }
0549             message = "Use the constructor taking a QScreen * instead.";
0550             warningLocation = stmt->getBeginLoc();
0551             emitWarning(warningLocation, message, fixits);
0552             return;
0553         }
0554         if (constructor->getDeclName().getAsString() != "QDateTime") {
0555             return;
0556         }
0557         if (consExpr->getNumArgs() != 1) {
0558             return;
0559         }
0560         if (consExpr->getArg(0)->getType().getAsString(lo()) != "const QDate") {
0561             return;
0562         }
0563         Stmt *child = clazy::childAt(stmt, 0);
0564         DeclRefExpr *decl;
0565         while (child) {
0566             decl = dyn_cast<DeclRefExpr>(child);
0567             if (!decl) {
0568                 child = clazy::childAt(child, 0);
0569                 continue;
0570             }
0571             break;
0572         }
0573         if (!decl) {
0574             return;
0575         }
0576 
0577         replacement = decl->getNameInfo().getAsString();
0578         QualType qualtype = decl->getType();
0579         if (qualtype->isPointerType()) {
0580             replacement += "->";
0581         } else {
0582             replacement += ".";
0583         }
0584         replacement += "startOfDay()";
0585 
0586         warningLocation = stmt->getBeginLoc();
0587         fixitRange = stmt->getSourceRange();
0588         message = "deprecated constructor. Use QDate::startOfDay() instead.";
0589     } else if (oppCallExpr) {
0590         if (clazy::isOfClass(oppCallExpr, "QDir")) {
0591             fixForDeprecatedOperator(stmt, "QDir");
0592             return;
0593         }
0594         if (clazy::isOfClass(oppCallExpr, "QVariant")) {
0595             fixForDeprecatedOperator(stmt, "QVariant");
0596             return;
0597         }
0598         return;
0599 
0600     } else if (declRefExp) {
0601         warningLocation = declRefExp->getBeginLoc();
0602         auto *decl = declRefExp->getDecl();
0603         if (!decl) {
0604             return;
0605         }
0606         auto *declContext = declRefExp->getDecl()->getDeclContext();
0607         if (!declContext) {
0608             return;
0609         }
0610         std::string functionName = declRefExp->getNameInfo().getAsString();
0611         std::string enclosingNameSpace;
0612         auto *enclNsContext = declContext->getEnclosingNamespaceContext();
0613         if (enclNsContext) {
0614             if (auto *ns = dyn_cast<NamespaceDecl>(declContext->getEnclosingNamespaceContext())) {
0615                 enclosingNameSpace = ns->getNameAsString();
0616             }
0617         }
0618 
0619         // To catch QDir and QSet
0620         std::string contextName;
0621         if (declContext) {
0622             if (clang::isa<clang::CXXRecordDecl>(declContext)) {
0623                 auto *recordDecl = llvm::dyn_cast<clang::CXXRecordDecl>(declContext);
0624                 contextName = recordDecl->getQualifiedNameAsString();
0625             }
0626         }
0627         if (isQSetDepreprecatedOperator(functionName, contextName, message)) {
0628             emitWarning(warningLocation, message, fixits);
0629             return;
0630         }
0631         if (functionName == "addResourceSearchPath" && contextName == "QDir") {
0632             message = "call function QDir::addResourceSearchPath(). Use function QDir::addSearchPath() with prefix instead";
0633             emitWarning(warningLocation, message, fixits);
0634             return;
0635         }
0636         if (functionName == "qrand" || functionName == "qsrand") {
0637             // To catch qrand and qsrand from qglobal.
0638             message = "use QRandomGenerator instead";
0639             emitWarning(warningLocation, message, fixits);
0640             return;
0641         }
0642 
0643         std::string declType;
0644         declType = decl->getType().getAsString();
0645         if (functionName == "AdjustToMinimumContentsLength" && declType == "enum QComboBox::SizeAdjustPolicy") {
0646             message = "Use QComboBox::SizeAdjustPolicy::AdjustToContents or AdjustToContentsOnFirstShow instead";
0647             emitWarning(warningLocation, message, fixits);
0648             return;
0649         }
0650 
0651         if (functionName == "AllDockWidgetFeatures" && declType == "enum QDockWidget::DockWidgetFeature") {
0652             message = "Use QComboBox::DockWidgetClosable|DockWidgetMovable|DockWidgetFloatable explicitly instead.";
0653             emitWarning(warningLocation, message, fixits);
0654             return;
0655         }
0656 
0657         if ((qStylePixelMetrix.find(functionName) != qStylePixelMetrix.end() && declType == "enum QStyle::PixelMetric")
0658             || (functionName == "SE_DialogButtonBoxLayoutItem" && declType == "enum QStyle::SubElement")) {
0659             message = "this enum has been removed in Qt6";
0660             emitWarning(warningLocation, message, fixits);
0661             return;
0662         }
0663 
0664         if (functionName != "MatchRegExp" && enclosingNameSpace != "QTextStreamFunctions" && functionName != "KeepEmptyParts"
0665             && functionName != "SkipEmptyParts") {
0666             return;
0667         }
0668 
0669         // To catch enum Qt::MatchFlag and enum QString::SplitBehavior
0670         // Get out of this DeclRefExp to catch the potential Qt namespace surrounding it
0671         bool isQtNamespaceExplicit = false;
0672         DeclContext *newcontext = clazy::contextForDecl(m_context->lastDecl);
0673         while (newcontext) {
0674             if (!newcontext) {
0675                 break;
0676             }
0677             if (clang::isa<NamespaceDecl>(newcontext)) {
0678                 auto *namesdecl = dyn_cast<clang::NamespaceDecl>(newcontext);
0679                 if (namesdecl->getNameAsString() == "Qt") {
0680                     isQtNamespaceExplicit = true;
0681                 }
0682             }
0683             newcontext = newcontext->getParent();
0684         }
0685 
0686         if (enclosingNameSpace == "QTextStreamFunctions") {
0687             replacementForQTextStreamFunctions(functionName, message, replacement, isQtNamespaceExplicit);
0688         } else if ((functionName == "KeepEmptyParts" || functionName == "SkipEmptyParts") && declType == "enum QString::SplitBehavior") {
0689             replacementForQStringSplitBehavior(functionName, message, replacement, isQtNamespaceExplicit);
0690         } else if (functionName == "MatchRegExp" && declType == "enum Qt::MatchFlag") {
0691             message = "call Qt::MatchRegExp. Use Qt::MatchRegularExpression instead.";
0692             if (isQtNamespaceExplicit) {
0693                 replacement = "MatchRegularExpression";
0694             } else {
0695                 replacement = "Qt::MatchRegularExpression";
0696             }
0697         } else {
0698             return;
0699         }
0700         fixitRange = declRefExp->getSourceRange();
0701 
0702     } else if (membExpr) {
0703         Stmt *child = clazy::childAt(stmt, 0);
0704         DeclRefExpr *decl = nullptr;
0705         while (child) {
0706             decl = dyn_cast<DeclRefExpr>(child);
0707             if (decl) {
0708                 break;
0709             }
0710             child = clazy::childAt(child, 0);
0711         }
0712 
0713         if (!decl) {
0714             return;
0715         }
0716         std::string functionName = membExpr->getMemberNameInfo().getAsString();
0717         std::string className = decl->getType().getAsString(lo());
0718         warningLocation = membExpr->getEndLoc();
0719 
0720         if (clazy::startsWith(className, "QMap<") && qMapFunctions.find(functionName) != qMapFunctions.end()) {
0721             message = "Use QMultiMap for maps storing multiple values with the same key.";
0722             emitWarning(warningLocation, message, fixits);
0723             return;
0724         }
0725         if (clazy::startsWith(className, "QHash<") && qMapFunctions.find(functionName) != qMapFunctions.end()) {
0726             // the name of the deprecated function are the same
0727             message = "Use QMultiHash for maps storing multiple values with the same key.";
0728             emitWarning(warningLocation, message, fixits);
0729             return;
0730         } else if (clazy::startsWith(className, "QDir") && functionName == "addResourceSearchPath") {
0731             message = "call function QDir::addResourceSearchPath(). Use function QDir::addSearchPath() with prefix instead";
0732             emitWarning(warningLocation, message, fixits);
0733             return;
0734         } else if (clazy::startsWith(className, "QTimeLine") && (functionName == "curveShape" || functionName == "setCurveShape")) {
0735             if (functionName == "curveShape") {
0736                 message = "call QTimeLine::curveShape. Use QTimeLine::easingCurve instead";
0737             } else {
0738                 message = "call QTimeLine::setCurveShape. Use QTimeLine::setEasingCurve instead";
0739             }
0740             emitWarning(warningLocation, message, fixits);
0741             return;
0742         } else if (clazy::startsWith(className, "QSet") && qSetDeprecatedFunctions.find(functionName) != qSetDeprecatedFunctions.end()) {
0743             message = "QSet iterator categories changed from bidirectional to forward. Please port your code manually";
0744             emitWarning(warningLocation, message, fixits);
0745             return;
0746         } else if (clazy::startsWith(className, "QHash") && qHashDeprecatedFunctions.find(functionName) != qHashDeprecatedFunctions.end()) {
0747             message = "QHash iterator categories changed from bidirectional to forward. Please port your code manually";
0748             emitWarning(warningLocation, message, fixits);
0749             return;
0750         } else if (clazy::startsWith(className, "QComboBox") && functionName == "currentIndexChanged") {
0751             if (!warningForQComboBox(membExpr, message, lo())) {
0752                 return;
0753             }
0754             emitWarning(warningLocation, message, fixits);
0755             return;
0756         } else if (clazy::startsWith(className, "QTextBrowser") && functionName == "highlighted") {
0757             if (!warningForQTextBrowser(membExpr, message, lo())) {
0758                 return;
0759             }
0760             emitWarning(warningLocation, message, fixits);
0761             return;
0762         } else if (clazy::startsWith(className, "QGraphicsView") && qGraphicsViewFunctions.find(functionName) != qMapFunctions.end()) {
0763             warningForGraphicsViews(functionName, message);
0764             emitWarning(warningLocation, message, fixits);
0765             return;
0766         } else if (className == "QDate" && functionName == "toString") {
0767             Stmt *parent = clazy::parent(m_context->parentMap, stmt);
0768             if (!replacementForQDate(parent, message, replacement, warningLocation, fixitRange, lo())) {
0769                 return;
0770             }
0771             fixits.push_back(FixItHint::CreateReplacement(fixitRange, replacement));
0772             emitWarning(warningLocation, message, fixits);
0773             return;
0774         } else if (clazy::startsWith(className, "QProcess") && qProcessDeprecatedFunctions.find(functionName) != qProcessDeprecatedFunctions.end()) {
0775             replacementForQProcess(functionName, message, replacement);
0776         } else if (clazy::startsWith(className, "QResource") && functionName == "isCompressed") {
0777             replacementForQResource(functionName, message, replacement);
0778         } else if (clazy::startsWith(className, "QSignalMapper") && functionName == "mapped") {
0779             replacementForQSignalMapper(membExpr, message, replacement, lo());
0780         } else if (clazy::startsWith(className, "QWizard") && functionName == "visitedPages") {
0781             replacementForQWizard(functionName, message, replacement);
0782         } else if (clazy::startsWith(className, "QButtonGroup")
0783                    && qButtonGroupDeprecatedFunctions.find(functionName) != qButtonGroupDeprecatedFunctions.end()) {
0784             if (!replacementForQButtonGroup(membExpr, message, replacement)) {
0785                 return;
0786             }
0787         } else if (clazy::startsWith(className, "QComboBox") && (functionName == "activated" || functionName == "highlighted")) {
0788             if (!replacementForQComboBox(membExpr, functionName, message, replacement, lo())) {
0789                 return;
0790             }
0791         } else {
0792             return;
0793         }
0794         fixitRange = SourceRange(membExpr->getEndLoc());
0795     } else {
0796         return;
0797     }
0798 
0799     fixits.push_back(FixItHint::CreateReplacement(fixitRange, replacement));
0800     emitWarning(warningLocation, message, fixits);
0801 
0802     return;
0803 }
0804 
0805 void Qt6DeprecatedAPIFixes::VisitMacroExpands(const clang::Token & /*MacroNameTok*/, const clang::SourceRange &range, const MacroInfo *)
0806 {
0807     m_listingMacroExpand.push_back(range.getBegin());
0808     return;
0809 }