File indexing completed on 2024-04-28 16:57:51

0001 /*
0002     This file is part of the clazy static checker.
0003 
0004     Copyright (C) 2015-2016 Sergio Martins <smartins@kde.org>
0005 
0006     This library is free software; you can redistribute it and/or
0007     modify it under the terms of the GNU Library General Public
0008     License as published by the Free Software Foundation; either
0009     version 2 of the License, or (at your option) any later version.
0010 
0011     This library is distributed in the hope that it will be useful,
0012     but WITHOUT ANY WARRANTY; without even the implied warranty of
0013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014     Library General Public License for more details.
0015 
0016     You should have received a copy of the GNU Library General Public License
0017     along with this library; see the file COPYING.LIB.  If not, write to
0018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019     Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #include "clazy_stl.h"
0023 #include "QtUtils.h"
0024 #include "Utils.h"
0025 #include "TypeUtils.h"
0026 #include "StmtBodyRange.h"
0027 #include "StringUtils.h"
0028 
0029 #include <clang/AST/ExprCXX.h>
0030 #include <clang/Basic/LLVM.h>
0031 #include <llvm/ADT/ArrayRef.h>
0032 #include <llvm/Support/raw_ostream.h>
0033 
0034 using namespace std;
0035 using namespace clang;
0036 
0037 bool clazy::isQtIterableClass(clang::CXXRecordDecl *record)
0038 {
0039     if (!record)
0040         return false;
0041 
0042     return isQtIterableClass(record->getQualifiedNameAsString());
0043 }
0044 
0045 const vector<StringRef> & clazy::qtContainers()
0046 {
0047     static const vector<StringRef> classes = { "QListSpecialMethods", "QList", "QVector", "QVarLengthArray", "QMap",
0048                                                "QHash", "QMultiMap", "QMultiHash", "QSet", "QStack", "QQueue", "QString", "QStringRef",
0049                                                "QByteArray", "QSequentialIterable", "QAssociativeIterable", "QJsonArray", "QLinkedList" };
0050     return classes;
0051 }
0052 
0053 const vector<StringRef> & clazy::qtCOWContainers()
0054 {
0055     static const vector<StringRef> classes = { "QListSpecialMethods", "QList", "QVector", "QMap", "QHash",
0056                                                "QMultiMap", "QMultiHash", "QSet", "QStack", "QQueue", "QString", "QStringRef",
0057                                                "QByteArray", "QJsonArray", "QLinkedList" };
0058     return classes;
0059 }
0060 
0061 
0062 std::unordered_map<string, std::vector<StringRef>> clazy::detachingMethods()
0063 {
0064     static std::unordered_map<string, std::vector<StringRef>> map;
0065     if (map.empty()) {
0066         map = detachingMethodsWithConstCounterParts();
0067         map["QVector"].push_back("fill");
0068     }
0069 
0070     return map;
0071 }
0072 
0073 std::unordered_map<string, std::vector<StringRef>> clazy::detachingMethodsWithConstCounterParts()
0074 {
0075     static std::unordered_map<string, std::vector<StringRef>> map;
0076     if (map.empty()) {
0077         map["QList"] = {"first", "last", "begin", "end", "front", "back", "operator[]"};
0078         map["QVector"] = {"first", "last", "begin", "end", "front", "back", "data", "operator[]" };
0079         map["QMap"] = {"begin", "end", "first", "find", "last", "operator[]", "lowerBound", "upperBound" };
0080         map["QHash"] = {"begin", "end", "find", "operator[]" };
0081         map["QLinkedList"] = {"first", "last", "begin", "end", "front", "back", "operator[]" };
0082         map["QSet"] = {"begin", "end", "find", "operator[]" };
0083         map["QStack"] = map["QVector"];
0084         map["QStack"].push_back({"top"});
0085         map["QQueue"] = map["QVector"];
0086         map["QQueue"].push_back({"head"});
0087         map["QMultiMap"] = map["QMap"];
0088         map["QMultiHash"] = map["QHash"];
0089         map["QString"] = {"begin", "end", "data", "operator[]"};
0090         map["QByteArray"] = {"data", "operator[]"};
0091         map["QImage"] = {"bits", "scanLine"};
0092     }
0093 
0094     return map;
0095 }
0096 
0097 bool clazy::isQtCOWIterableClass(clang::CXXRecordDecl *record)
0098 {
0099     if (!record)
0100         return false;
0101 
0102     return isQtCOWIterableClass(record->getQualifiedNameAsString());
0103 }
0104 
0105 bool clazy::isQtCOWIterableClass(const string &className)
0106 {
0107     const auto &classes = qtCOWContainers();
0108     return clazy::contains(classes, className);
0109 }
0110 
0111 bool clazy::isQtIterableClass(StringRef className)
0112 {
0113     const auto &classes = qtContainers();
0114     return clazy::contains(classes, className);
0115 }
0116 
0117 bool clazy::isQtAssociativeContainer(clang::CXXRecordDecl *record)
0118 {
0119     if (!record)
0120         return false;
0121 
0122     return isQtAssociativeContainer(record->getNameAsString());
0123 }
0124 
0125 bool clazy::isQtAssociativeContainer(StringRef className)
0126 {
0127     static const vector<StringRef> classes = { "QSet", "QMap", "QHash" };
0128     return clazy::contains(classes, className);
0129 }
0130 
0131 bool clazy::isQObject(const CXXRecordDecl *decl)
0132 {
0133     return clazy::derivesFrom(decl, "QObject");
0134 }
0135 
0136 bool clazy::isQObject(clang::QualType qt)
0137 {
0138     qt = clazy::pointeeQualType(qt);
0139     const auto t = qt.getTypePtrOrNull();
0140     return t ? isQObject(t->getAsCXXRecordDecl()) : false;
0141 }
0142 
0143 bool clazy::isConvertibleTo(const Type *source, const Type *target)
0144 {
0145     if (!source || !target)
0146         return false;
0147 
0148     if (source->isPointerType() ^ target->isPointerType())
0149         return false;
0150 
0151     if (source == target)
0152         return true;
0153 
0154     if (source->getPointeeCXXRecordDecl() && source->getPointeeCXXRecordDecl() == target->getPointeeCXXRecordDecl())
0155         return true;
0156 
0157     if (source->isIntegerType() && target->isIntegerType())
0158         return true;
0159 
0160     if (source->isFloatingType() && target->isFloatingType())
0161         return true;
0162 
0163     // "QString" can convert to "const QString &" and vice versa
0164     if (clazy::isConstRef(source) && source->getPointeeType().getTypePtrOrNull() == target)
0165         return true;
0166     if (clazy::isConstRef(target) && target->getPointeeType().getTypePtrOrNull() == source)
0167         return true;
0168 
0169     return false;
0170 }
0171 
0172 bool clazy::isJavaIterator(CXXRecordDecl *record)
0173 {
0174     if (!record)
0175         return false;
0176 
0177     static const vector<StringRef> names = { "QHashIterator", "QMapIterator", "QSetIterator", "QListIterator",
0178                                              "QVectorIterator", "QLinkedListIterator", "QStringListIterator" };
0179 
0180     return clazy::contains(names, clazy::name(record));
0181 }
0182 
0183 bool clazy::isJavaIterator(CXXMemberCallExpr *call)
0184 {
0185     if (!call)
0186         return false;
0187 
0188     return isJavaIterator(call->getRecordDecl());
0189 }
0190 
0191 bool clazy::isQtContainer(QualType t)
0192 {
0193     CXXRecordDecl *record = clazy::typeAsRecord(t);
0194     if (!record)
0195         return false;
0196 
0197     return isQtContainer(record);
0198 }
0199 
0200 bool clazy::isQtContainer(const CXXRecordDecl *record)
0201 {
0202     const StringRef typeName = clazy::name(record);
0203     return clazy::any_of(clazy::qtContainers(), [typeName] (StringRef container) {
0204         return container == typeName;
0205     });
0206 }
0207 
0208 bool clazy::isAReserveClass(CXXRecordDecl *recordDecl)
0209 {
0210     if (!recordDecl)
0211         return false;
0212 
0213     static const std::vector<std::string> classes = {"QVector", "std::vector", "QList", "QSet"};
0214 
0215     return clazy::any_of(classes, [recordDecl](const string &className) {
0216         return clazy::derivesFrom(recordDecl, className);
0217     });
0218 }
0219 
0220 clang::CXXRecordDecl *clazy::getQObjectBaseClass(clang::CXXRecordDecl *recordDecl)
0221 {
0222     if (!recordDecl)
0223         return nullptr;
0224 
0225     for (auto baseClass : recordDecl->bases()) {
0226         CXXRecordDecl *record = clazy::recordFromBaseSpecifier(baseClass);
0227         if (isQObject(record))
0228             return record;
0229     }
0230 
0231     return nullptr;
0232 }
0233 
0234 
0235 bool clazy::isConnect(FunctionDecl *func)
0236 {
0237     return func && func->getQualifiedNameAsString() == "QObject::connect";
0238 }
0239 
0240 bool clazy::connectHasPMFStyle(FunctionDecl *func)
0241 {
0242     // Look for char* arguments
0243     for (auto parm : Utils::functionParameters(func)) {
0244         QualType qt = parm->getType();
0245         const Type *t = qt.getTypePtrOrNull();
0246         if (!t || !t->isPointerType())
0247             continue;
0248 
0249         const Type *ptt = t->getPointeeType().getTypePtrOrNull();
0250         if (ptt && ptt->isCharType())
0251             return false;
0252     }
0253 
0254     return true;
0255 }
0256 
0257 CXXMethodDecl *clazy::pmfFromConnect(CallExpr *funcCall, int argIndex)
0258 {
0259     if (!funcCall)
0260         return nullptr;
0261 
0262     const int numArgs = funcCall->getNumArgs();
0263     if (numArgs < 3) {
0264         llvm::errs() << "error, connect call has less than 3 arguments\n";
0265         return nullptr;
0266     }
0267 
0268     if (argIndex >= numArgs)
0269         return nullptr;
0270 
0271     Expr *expr = funcCall->getArg(argIndex);
0272     return pmfFromUnary(expr);
0273 }
0274 
0275 
0276 CXXMethodDecl *clazy::pmfFromUnary(Expr *expr)
0277 {
0278     if (auto uo = dyn_cast<UnaryOperator>(expr)) {
0279         return pmfFromUnary(uo);
0280     } else if (auto call = dyn_cast<CXXOperatorCallExpr>(expr)) {
0281 
0282         if (call->getNumArgs() <= 1)
0283             return nullptr;
0284 
0285         FunctionDecl *func = call->getDirectCallee();
0286         if (!func)
0287             return nullptr;
0288 
0289         auto context = func->getParent();
0290         if (!context)
0291             return nullptr;
0292 
0293         auto record = dyn_cast<CXXRecordDecl>(context);
0294         if (!record)
0295             return nullptr;
0296 
0297         const std::string className = record->getQualifiedNameAsString();
0298         if (className != "QNonConstOverload" && className != "QConstOverload")
0299             return nullptr;
0300 
0301         return pmfFromUnary(dyn_cast<UnaryOperator>(call->getArg(1)));
0302     } else if (auto staticCast = dyn_cast<CXXStaticCastExpr>(expr)) {
0303         return pmfFromUnary(staticCast->getSubExpr());
0304     } else if (auto callexpr = dyn_cast<CallExpr>(expr)) {
0305         // QOverload case, go deeper one level to get to the UnaryOperator
0306         if (callexpr->getNumArgs() == 1)
0307             return pmfFromUnary(callexpr->getArg(0));
0308     } else if (auto impl = dyn_cast<ImplicitCastExpr>(expr)) {
0309         return pmfFromUnary(impl->getSubExpr());
0310     }
0311 
0312     return nullptr;
0313 }
0314 
0315 CXXMethodDecl *clazy::pmfFromUnary(UnaryOperator *uo)
0316 {
0317     if (!uo)
0318         return nullptr;
0319 
0320     Expr *subExpr = uo->getSubExpr();
0321     if (!subExpr)
0322         return nullptr;
0323 
0324     auto declref = dyn_cast<DeclRefExpr>(subExpr);
0325 
0326     if (declref)
0327         return dyn_cast<CXXMethodDecl>(declref->getDecl());
0328 
0329     return nullptr;
0330 }
0331 
0332 bool clazy::recordHasCtorWithParam(clang::CXXRecordDecl *record, const std::string &paramType, bool &ok, int &numCtors)
0333 {
0334     ok = true;
0335     numCtors = 0;
0336     if (!record || !record->hasDefinition() ||
0337         record->getDefinition() != record) { // Means fwd decl
0338         ok = false;
0339         return false;
0340     }
0341 
0342     for (auto ctor : record->ctors()) {
0343         if (ctor->isCopyOrMoveConstructor())
0344             continue;
0345         numCtors++;
0346         for (auto param : ctor->parameters()) {
0347             QualType qt = clazy::pointeeQualType(param->getType());
0348             if (!qt.isConstQualified() && clazy::derivesFrom(qt, paramType)) {
0349                 return true;
0350             }
0351         }
0352     }
0353 
0354     return false;
0355 }