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 ¶mType, 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 }