File indexing completed on 2024-04-14 05:32:08
0001 /* 0002 SPDX-FileCopyrightText: 2015-2016 Sergio Martins <smartins@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "QtUtils.h" 0008 #include "FunctionUtils.h" 0009 #include "MacroUtils.h" 0010 #include "StringUtils.h" 0011 #include "TypeUtils.h" 0012 #include "Utils.h" 0013 #include "clazy_stl.h" 0014 0015 #include <clang/AST/ExprCXX.h> 0016 #include <clang/Basic/LLVM.h> 0017 #include <llvm/ADT/ArrayRef.h> 0018 #include <llvm/Support/raw_ostream.h> 0019 0020 using namespace clang; 0021 0022 bool clazy::isQtIterableClass(clang::CXXRecordDecl *record) 0023 { 0024 return record && isQtIterableClass(record->getQualifiedNameAsString()); 0025 } 0026 0027 const std::vector<StringRef> &clazy::qtContainers() 0028 { 0029 static const std::vector<StringRef> classes = { 0030 "QListSpecialMethods", 0031 "QListSpecialMethodsBase", // Qt6 0032 "QList", 0033 "QVector", 0034 "QVarLengthArray", 0035 "QMap", 0036 "QHash", 0037 "QMultiMap", 0038 "QMultiHash", 0039 "QSet", 0040 "QStack", 0041 "QQueue", 0042 "QString", 0043 "QStringRef", 0044 "QByteArray", 0045 "QSequentialIterable", 0046 "QAssociativeIterable", 0047 "QJsonArray", 0048 "QJsonObject", 0049 "QLinkedList", 0050 }; 0051 return classes; 0052 } 0053 0054 const std::vector<StringRef> &clazy::qtCOWContainers() 0055 { 0056 static const std::vector<StringRef> classes = { 0057 "QListSpecialMethods", 0058 "QListSpecialMethodsBase", // Qt6 0059 "QList", 0060 "QVector", 0061 "QMap", 0062 "QHash", 0063 "QMultiMap", 0064 "QMultiHash", 0065 "QSet", 0066 "QStack", 0067 "QQueue", 0068 "QString", 0069 "QStringRef", 0070 "QByteArray", 0071 "QJsonArray", 0072 "QJsonObject", 0073 "QLinkedList", 0074 }; 0075 return classes; 0076 } 0077 0078 std::unordered_map<std::string, std::vector<StringRef>> clazy::detachingMethods() 0079 { 0080 static std::unordered_map<std::string, std::vector<StringRef>> map; 0081 if (map.empty()) { 0082 map = detachingMethodsWithConstCounterParts(); 0083 map["QVector"].push_back("fill"); 0084 } 0085 0086 return map; 0087 } 0088 0089 std::unordered_map<std::string, std::vector<StringRef>> clazy::detachingMethodsWithConstCounterParts() 0090 { 0091 static std::unordered_map<std::string, std::vector<StringRef>> map; 0092 if (map.empty()) { 0093 map["QList"] = {"first", "last", "begin", "end", "front", "back", "operator[]"}; 0094 map["QVector"] = {"first", "last", "begin", "end", "front", "back", "data", "operator[]"}; 0095 map["QMap"] = {"begin", "end", "first", "find", "last", "operator[]", "lowerBound", "upperBound"}; 0096 map["QHash"] = {"begin", "end", "find", "operator[]"}; 0097 map["QLinkedList"] = {"first", "last", "begin", "end", "front", "back", "operator[]"}; 0098 map["QSet"] = {"begin", "end", "find", "operator[]"}; 0099 map["QStack"] = map["QVector"]; 0100 map["QStack"].push_back({"top"}); 0101 map["QQueue"] = map["QVector"]; 0102 map["QQueue"].push_back({"head"}); 0103 map["QMultiMap"] = map["QMap"]; 0104 map["QMultiHash"] = map["QHash"]; 0105 map["QString"] = {"begin", "end", "data", "operator[]"}; 0106 map["QByteArray"] = {"data", "operator[]"}; 0107 map["QImage"] = {"bits", "scanLine"}; 0108 map["QJsonObject"] = {"begin", "end", "operator[]", "find"}; 0109 } 0110 0111 return map; 0112 } 0113 0114 bool clazy::isQMetaMethod(CallExpr *Call, unsigned int argIndex) 0115 { 0116 Expr *arg = Call->getArg(argIndex); 0117 QualType type = arg->getType(); 0118 if (!type->isRecordType()) { 0119 return false; 0120 } 0121 0122 CXXRecordDecl *recordDecl = type->getAsCXXRecordDecl(); 0123 if (!recordDecl) { 0124 return false; 0125 } 0126 0127 return recordDecl->getQualifiedNameAsString() == "QMetaMethod"; 0128 } 0129 0130 bool clazy::isQtCOWIterableClass(clang::CXXRecordDecl *record) 0131 { 0132 return record && isQtCOWIterableClass(record->getQualifiedNameAsString()); 0133 } 0134 0135 bool clazy::isQtCOWIterableClass(const std::string &className) 0136 { 0137 const auto &classes = qtCOWContainers(); 0138 return clazy::contains(classes, className); 0139 } 0140 0141 bool clazy::isQtIterableClass(StringRef className) 0142 { 0143 const auto &classes = qtContainers(); 0144 return clazy::contains(classes, className); 0145 } 0146 0147 bool clazy::isQtAssociativeContainer(clang::CXXRecordDecl *record) 0148 { 0149 return record && isQtAssociativeContainer(record->getNameAsString()); 0150 } 0151 0152 bool clazy::isQtAssociativeContainer(StringRef className) 0153 { 0154 static const std::vector<StringRef> classes = {"QSet", "QMap", "QHash", "QMultiHash", "QMultiMap"}; 0155 return clazy::contains(classes, className); 0156 } 0157 0158 bool clazy::isQObject(const CXXRecordDecl *decl) 0159 { 0160 return clazy::derivesFrom(decl, "QObject"); 0161 } 0162 0163 bool clazy::isQObject(clang::QualType qt) 0164 { 0165 qt = clazy::pointeeQualType(qt); 0166 const auto *const t = qt.getTypePtrOrNull(); 0167 return t ? isQObject(t->getAsCXXRecordDecl()) : false; 0168 } 0169 0170 bool clazy::isConvertibleTo(const Type *source, const Type *target) 0171 { 0172 if (!source || !target) { 0173 return false; 0174 } 0175 0176 if (source->isPointerType() ^ target->isPointerType()) { 0177 return false; 0178 } 0179 0180 if (source == target) { 0181 return true; 0182 } 0183 0184 if (source->getPointeeCXXRecordDecl() && source->getPointeeCXXRecordDecl() == target->getPointeeCXXRecordDecl()) { 0185 return true; 0186 } 0187 0188 if (source->isIntegerType() && target->isIntegerType()) { 0189 return true; 0190 } 0191 0192 if (source->isFloatingType() && target->isFloatingType()) { 0193 return true; 0194 } 0195 0196 // "QString" can convert to "const QString &" and vice versa 0197 if (clazy::isConstRef(source) && source->getPointeeType().getTypePtrOrNull() == target) { 0198 return true; 0199 } 0200 if (clazy::isConstRef(target) && target->getPointeeType().getTypePtrOrNull() == source) { 0201 return true; 0202 } 0203 0204 return false; 0205 } 0206 0207 bool clazy::isJavaIterator(CXXRecordDecl *record) 0208 { 0209 if (!record) { 0210 return false; 0211 } 0212 0213 static const std::vector<StringRef> names = { 0214 "QHashIterator", 0215 "QMapIterator", 0216 "QSetIterator", 0217 "QListIterator", 0218 "QVectorIterator", // typedef in Qt6 0219 "QStringListIterator", // typedef in Qt6 0220 "QLinkedListIterator", // removed in Qt6 0221 }; 0222 0223 return clazy::contains(names, clazy::name(record)); 0224 } 0225 0226 bool clazy::isJavaIterator(CXXMemberCallExpr *call) 0227 { 0228 return call && isJavaIterator(call->getRecordDecl()); 0229 } 0230 0231 bool clazy::isQtContainer(QualType t) 0232 { 0233 CXXRecordDecl *record = clazy::typeAsRecord(t); 0234 return record && isQtContainer(record); 0235 } 0236 0237 bool clazy::isQtContainer(const CXXRecordDecl *record) 0238 { 0239 const StringRef typeName = clazy::name(record); 0240 return clazy::any_of(clazy::qtContainers(), [typeName](StringRef container) { 0241 return container == typeName; 0242 }); 0243 } 0244 0245 bool clazy::isAReserveClass(CXXRecordDecl *recordDecl) 0246 { 0247 if (!recordDecl) { 0248 return false; 0249 } 0250 0251 static const std::vector<std::string> classes = {"QVector", "std::vector", "QList", "QSet"}; 0252 return clazy::any_of(classes, [recordDecl](const std::string &className) { 0253 return clazy::derivesFrom(recordDecl, className); 0254 }); 0255 } 0256 0257 clang::CXXRecordDecl *clazy::getQObjectBaseClass(clang::CXXRecordDecl *recordDecl) 0258 { 0259 if (!recordDecl) { 0260 return nullptr; 0261 } 0262 0263 for (auto baseClass : recordDecl->bases()) { 0264 if (CXXRecordDecl *record = clazy::recordFromBaseSpecifier(baseClass); isQObject(record)) { 0265 return record; 0266 } 0267 } 0268 0269 return nullptr; 0270 } 0271 0272 bool clazy::isConnect(FunctionDecl *func) 0273 { 0274 return func && func->getQualifiedNameAsString() == "QObject::connect"; 0275 } 0276 0277 bool clazy::connectHasPMFStyle(FunctionDecl *func) 0278 { 0279 // Look for char* arguments 0280 for (auto *parm : Utils::functionParameters(func)) { 0281 QualType qt = parm->getType(); 0282 const Type *t = qt.getTypePtrOrNull(); 0283 if (!t || !t->isPointerType()) { 0284 continue; 0285 } 0286 0287 const Type *ptt = t->getPointeeType().getTypePtrOrNull(); 0288 if (ptt && ptt->isCharType()) { 0289 return false; 0290 } 0291 } 0292 0293 return true; 0294 } 0295 0296 CXXMethodDecl *clazy::pmfFromConnect(CallExpr *funcCall, int argIndex) 0297 { 0298 if (!funcCall) { 0299 return nullptr; 0300 } 0301 0302 const int numArgs = funcCall->getNumArgs(); 0303 if (numArgs < 3) { 0304 llvm::errs() << "error, connect call has less than 3 arguments\n"; 0305 return nullptr; 0306 } 0307 0308 if (argIndex >= numArgs) { 0309 return nullptr; 0310 } 0311 0312 Expr *expr = funcCall->getArg(argIndex); 0313 return pmfFromExpr(expr); 0314 } 0315 0316 CXXMethodDecl *clazy::pmfFromExpr(Expr *expr) 0317 { 0318 if (auto *uo = dyn_cast<UnaryOperator>(expr)) { 0319 return pmfFromUnary(uo); 0320 } 0321 if (auto *call = dyn_cast<CXXOperatorCallExpr>(expr)) { 0322 if (call->getNumArgs() <= 1) { 0323 return nullptr; 0324 } 0325 0326 FunctionDecl *func = call->getDirectCallee(); 0327 if (!func) { 0328 return nullptr; 0329 } 0330 0331 auto *context = func->getParent(); 0332 if (!context) { 0333 return nullptr; 0334 } 0335 0336 auto *record = dyn_cast<CXXRecordDecl>(context); 0337 if (!record) { 0338 return nullptr; 0339 } 0340 0341 const std::string className = record->getQualifiedNameAsString(); 0342 if (className != "QNonConstOverload" && className != "QConstOverload") { 0343 return nullptr; 0344 } 0345 0346 return pmfFromUnary(dyn_cast<UnaryOperator>(call->getArg(1))); 0347 } else if (auto *staticCast = dyn_cast<CXXStaticCastExpr>(expr)) { 0348 return pmfFromExpr(staticCast->getSubExpr()); 0349 } else if (auto *callexpr = dyn_cast<CallExpr>(expr)) { 0350 // QOverload case, go deeper one level to get to the UnaryOperator 0351 if (callexpr->getNumArgs() == 1) { 0352 return pmfFromExpr(callexpr->getArg(0)); 0353 } 0354 } else if (auto *matTempExpr = dyn_cast<MaterializeTemporaryExpr>(expr)) { 0355 // Qt6 PMF, go deeper one level to get to the UnaryOperator 0356 return pmfFromExpr(matTempExpr->getSubExpr()); 0357 } else if (auto *impl = dyn_cast<ImplicitCastExpr>(expr)) { 0358 return pmfFromExpr(impl->getSubExpr()); 0359 } 0360 0361 return nullptr; 0362 } 0363 0364 CXXMethodDecl *clazy::pmfFromUnary(UnaryOperator *uo) 0365 { 0366 if (!uo) { 0367 return nullptr; 0368 } 0369 0370 Expr *subExpr = uo->getSubExpr(); 0371 if (!subExpr) { 0372 return nullptr; 0373 } 0374 0375 auto *declref = dyn_cast<DeclRefExpr>(subExpr); 0376 0377 if (declref) { 0378 return dyn_cast<CXXMethodDecl>(declref->getDecl()); 0379 } 0380 0381 return nullptr; 0382 } 0383 0384 bool clazy::recordHasCtorWithParam(clang::CXXRecordDecl *record, const std::string ¶mType, bool &ok, int &numCtors) 0385 { 0386 ok = true; 0387 numCtors = 0; 0388 if (!record || !record->hasDefinition() || record->getDefinition() != record) { // Means fwd decl 0389 ok = false; 0390 return false; 0391 } 0392 0393 for (auto *ctor : record->ctors()) { 0394 if (ctor->isCopyOrMoveConstructor()) { 0395 continue; 0396 } 0397 numCtors++; 0398 for (auto *param : ctor->parameters()) { 0399 QualType qt = clazy::pointeeQualType(param->getType()); 0400 if (!qt.isConstQualified() && clazy::derivesFrom(qt, paramType)) { 0401 return true; 0402 } 0403 } 0404 } 0405 0406 return false; 0407 } 0408 0409 clang::ValueDecl *clazy::signalReceiverForConnect(clang::CallExpr *call) 0410 { 0411 if (!call || call->getNumArgs() < 5) { 0412 return nullptr; 0413 } 0414 0415 return clazy::valueDeclForCallArgument(call, 2); 0416 } 0417 0418 clang::ValueDecl *clazy::signalSenderForConnect(clang::CallExpr *call) 0419 { 0420 return clazy::valueDeclForCallArgument(call, 0); 0421 } 0422 0423 bool clazy::isBootstrapping(const clang::PreprocessorOptions &ppOpts) 0424 { 0425 return clazy::isPredefined(ppOpts, "QT_BOOTSTRAPPED"); 0426 } 0427 0428 bool clazy::isInForeach(const clang::ASTContext *context, clang::SourceLocation loc) 0429 { 0430 return clazy::isInAnyMacro(context, loc, {"Q_FOREACH", "foreach"}); 0431 }