File indexing completed on 2024-05-12 05:40:57
0001 /* 0002 SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com 0003 SPDX-FileContributor: Waqar Ahmed <waqar.ahmed@kdab.com> 0004 SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com> 0005 SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "use-static-qregularexpression.h" 0011 #include "HierarchyUtils.h" 0012 #include "QtUtils.h" 0013 #include "TypeUtils.h" 0014 #include "Utils.h" 0015 0016 #include <clang/AST/AST.h> 0017 #include <clang/AST/ExprCXX.h> 0018 0019 using namespace clang; 0020 0021 UseStaticQRegularExpression::UseStaticQRegularExpression(const std::string &name, ClazyContext *context) 0022 : CheckBase(name, context) 0023 { 0024 } 0025 0026 static MaterializeTemporaryExpr *isArgTemporaryObj(Expr *arg0) 0027 { 0028 return dyn_cast_or_null<MaterializeTemporaryExpr>(arg0); 0029 } 0030 0031 static VarDecl *getVarDecl(Expr *arg) 0032 { 0033 auto *declRefExpr = dyn_cast<DeclRefExpr>(arg); 0034 declRefExpr = declRefExpr ? declRefExpr : clazy::getFirstChildOfType<DeclRefExpr>(arg); 0035 if (!declRefExpr) { 0036 return nullptr; 0037 } 0038 return dyn_cast_or_null<VarDecl>(declRefExpr->getDecl()); 0039 } 0040 0041 static Expr *getVarInitExpr(VarDecl *VDef) 0042 { 0043 return VDef->getDefinition() ? VDef->getDefinition()->getInit() : nullptr; 0044 } 0045 0046 static bool isQStringModifiedAfterCreation(clang::Stmt *expr, LangOptions lo) 0047 { 0048 // This QString is the result of a .arg/.mid or similar call, check all args if they are a literal 0049 if (auto *methodCall = clazy::getFirstChildOfType<CXXMemberCallExpr>(expr)) { 0050 if (auto *callee = methodCall->getMethodDecl()) { 0051 if (callee->getReturnType().getAsString(lo) == "QString") { 0052 return true; 0053 } 0054 } 0055 } 0056 return false; 0057 } 0058 0059 static bool isQStringFromStringLiteral(Expr *qstring, LangOptions lo) 0060 { 0061 if (isArgTemporaryObj(qstring)) { 0062 // Is it compile time known QString i.e., not from a function call 0063 auto *qstringCtor = clazy::getFirstChildOfType<CXXConstructExpr>(qstring); 0064 if (!qstringCtor) { 0065 return false; 0066 } 0067 0068 return clazy::getFirstChildOfType<StringLiteral>(qstringCtor); 0069 } 0070 0071 if (auto *VD = getVarDecl(qstring)) { 0072 auto *stringLit = clazy::getFirstChildOfType<StringLiteral>(getVarInitExpr(VD)); 0073 if (stringLit) { 0074 // If we have a string literal somewhere in there, but modify it using QString::arg or friends, we don't have a literal for th regex 0075 if (auto *constructExpr = clazy::getFirstChildOfType<CXXConstructExpr>(VD->getInit())) { 0076 return !isQStringModifiedAfterCreation(constructExpr, lo); 0077 } else { 0078 return true; 0079 } 0080 } 0081 } 0082 return false; 0083 } 0084 0085 static bool isTemporaryQRegexObj(Expr *qregexVar, const LangOptions &lo) 0086 { 0087 // Get the QRegularExpression ctor 0088 auto *ctor = clazy::getFirstChildOfType<CXXConstructExpr>(qregexVar); 0089 if (!ctor || ctor->getNumArgs() == 0) { 0090 return false; 0091 } 0092 0093 // Check if its first arg is "QString" 0094 auto *qstrArg = ctor->getArg(0); 0095 if (!qstrArg || clazy::typeName(qstrArg->getType(), lo, true) != "QString") { 0096 return false; 0097 } 0098 0099 return isQStringFromStringLiteral(qstrArg, lo) && !isQStringModifiedAfterCreation(qstrArg, lo); 0100 } 0101 0102 static bool isQRegexpFromStringLiteral(VarDecl *qregexVarDecl, LangOptions lo) 0103 { 0104 Expr *initExpr = getVarInitExpr(qregexVarDecl); 0105 if (!initExpr) { 0106 return false; 0107 } 0108 0109 auto *ctorCall = dyn_cast<CXXConstructExpr>(initExpr); 0110 if (!ctorCall) { 0111 ctorCall = clazy::getFirstChildOfType<CXXConstructExpr>(initExpr); 0112 if (!ctorCall) { 0113 return false; 0114 } 0115 } 0116 0117 if (ctorCall->getNumArgs() < 2) { 0118 return false; 0119 } 0120 0121 auto *qstringArg = ctorCall->getArg(0); 0122 if (!qstringArg) { 0123 return false; 0124 } 0125 0126 // For C++17, we have to put in more effort to resolving the initialization 0127 if (auto *expr = clazy::getFirstChildOfType<DeclRefExpr>(qstringArg)) { 0128 if (auto *exprClean = dyn_cast<VarDecl>(expr->getDecl())) { 0129 if (isQStringModifiedAfterCreation(exprClean->getInit(), lo)) { 0130 return false; 0131 } 0132 } 0133 } 0134 return isQStringFromStringLiteral(qstringArg, lo) && !isQStringModifiedAfterCreation(qstringArg, lo); 0135 } 0136 0137 static bool isArgNonStaticLocalVar(Expr *qregexp, LangOptions lo) 0138 { 0139 auto *varDecl = getVarDecl(qregexp); 0140 if (!varDecl) { 0141 return false; 0142 } 0143 0144 if (!isQRegexpFromStringLiteral(varDecl, lo)) { 0145 return false; 0146 } 0147 0148 return varDecl->isLocalVarDecl() && !varDecl->isStaticLocal(); 0149 } 0150 0151 static bool isOfAcceptableType(CXXMethodDecl *methodDecl) 0152 { 0153 const auto type = clazy::classNameFor(methodDecl); 0154 return type == "QString" || type == "QStringList" || type == "QRegularExpression" || type == "QListSpecialMethods" /* for QStringList in Qt6 */; 0155 } 0156 0157 static bool firstArgIsQRegularExpression(CXXMethodDecl *methodDecl, const LangOptions &lo) 0158 { 0159 return clazy::simpleArgTypeName(methodDecl, 0, lo) == "QRegularExpression"; 0160 } 0161 0162 void UseStaticQRegularExpression::VisitStmt(clang::Stmt *stmt) 0163 { 0164 if (!stmt) { 0165 return; 0166 } 0167 0168 auto *method = dyn_cast_or_null<CXXMemberCallExpr>(stmt); 0169 if (!method) { 0170 return; 0171 } 0172 0173 if (method->getNumArgs() == 0) { 0174 return; 0175 } 0176 0177 auto *methodDecl = method->getMethodDecl(); 0178 if (!methodDecl || !methodDecl->getDeclName().isIdentifier()) { 0179 return; 0180 } 0181 0182 if (!isOfAcceptableType(methodDecl)) { 0183 return; 0184 } 0185 0186 // QRegularExpression.match() 0187 const auto methodName = methodDecl->getName(); 0188 if (methodName == "match" || methodName == "globalMatch") { 0189 auto *obj = method->getImplicitObjectArgument()->IgnoreImpCasts(); 0190 if (!obj) { 0191 return; 0192 } 0193 0194 if (obj->isLValue()) { 0195 if (isArgNonStaticLocalVar(obj, lo())) { 0196 emitWarning(obj->getBeginLoc(), "Don't create temporary QRegularExpression objects. Use a static QRegularExpression object instead"); 0197 return; 0198 } 0199 } else if (obj->isXValue()) { 0200 // is it a temporary? 0201 auto *temp = dyn_cast<MaterializeTemporaryExpr>(obj); 0202 if (!temp) { 0203 return; 0204 } 0205 if (isTemporaryQRegexObj(temp, lo())) { 0206 emitWarning(temp->getBeginLoc(), "Don't create temporary QRegularExpression objects. Use a static QRegularExpression object instead"); 0207 } 0208 } 0209 return; 0210 } 0211 0212 if (!firstArgIsQRegularExpression(methodDecl, lo())) { 0213 return; 0214 } 0215 0216 Expr *qregexArg = method->getArg(0); 0217 if (!qregexArg) { 0218 return; 0219 } 0220 0221 // Its a QString*().method(QRegularExpression(arg)) ? 0222 if (auto *temp = isArgTemporaryObj(qregexArg)) { 0223 if (isTemporaryQRegexObj(temp, lo())) { 0224 emitWarning(qregexArg->getBeginLoc(), "Don't create temporary QRegularExpression objects. Use a static QRegularExpression object instead"); 0225 } 0226 } 0227 0228 // Its a local QRegularExpression variable? 0229 if (isArgNonStaticLocalVar(qregexArg, lo())) { 0230 emitWarning(qregexArg->getBeginLoc(), "Don't create temporary QRegularExpression objects. Use a static QRegularExpression object instead"); 0231 } 0232 }