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 &paramType, 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 }