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 }