File indexing completed on 2024-04-28 16:57:52
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 #ifndef CLAZY_QT_UTILS_H 0023 #define CLAZY_QT_UTILS_H 0024 0025 #include "TypeUtils.h" 0026 #include "MacroUtils.h" 0027 #include "FunctionUtils.h" 0028 #include "StringUtils.h" 0029 #include "Utils.h" 0030 #include "clazy_stl.h" 0031 0032 #include <clang/AST/ASTContext.h> 0033 #include <clang/AST/Decl.h> 0034 #include <clang/AST/DeclCXX.h> 0035 #include <clang/AST/DeclTemplate.h> 0036 #include <clang/AST/Expr.h> 0037 #include <clang/AST/OperationKinds.h> 0038 #include <clang/AST/Stmt.h> 0039 #include <clang/AST/TemplateBase.h> 0040 #include <clang/AST/Type.h> 0041 #include <clang/Basic/SourceLocation.h> 0042 #include <llvm/ADT/StringRef.h> 0043 #include <llvm/Support/Casting.h> 0044 0045 #include <string> 0046 #include <vector> 0047 #include <unordered_map> 0048 0049 namespace clang { 0050 class CXXRecordDecl; 0051 class CompilerInstance; 0052 class Type; 0053 class CXXMemberCallExpr; 0054 class CallExpr; 0055 class ValueDecl; 0056 class LangOptions; 0057 class QualType; 0058 class VarDecl; 0059 class SourceLocation; 0060 class FunctionDecl; 0061 class UnaryOperator; 0062 class CXXMethodDecl; 0063 class Expr; 0064 class PreprocessorOptions; 0065 class SourceManager; 0066 } 0067 0068 struct StmtBodyRange; 0069 0070 namespace clazy 0071 { 0072 0073 /** 0074 * Returns true if the class is a Qt class which can be iterated with foreach. 0075 * Which means all containers and also stuff like QAssociativeIterable. 0076 */ 0077 bool isQtIterableClass(clang::CXXRecordDecl *record); 0078 0079 /** 0080 * Overload. 0081 */ 0082 bool isQtIterableClass(llvm::StringRef className); 0083 0084 /** 0085 * Returns true if the class is a Qt class which can be iterated with foreach and also implicitly shared. 0086 */ 0087 bool isQtCOWIterableClass(clang::CXXRecordDecl *record); 0088 0089 /** 0090 * Overload. 0091 */ 0092 bool isQtCOWIterableClass(const std::string &className); 0093 0094 /** 0095 * Returns if the iterators belongs to a COW container 0096 */ 0097 inline bool isQtCOWIterator(clang::CXXRecordDecl *itRecord) 0098 { 0099 if (!itRecord) 0100 return false; 0101 0102 auto parent = llvm::dyn_cast_or_null<clang::CXXRecordDecl>(itRecord->getParent()); 0103 return parent && clazy::isQtCOWIterableClass(parent); 0104 } 0105 0106 /** 0107 * Returns true if the class is a Qt class which is an associative container (QHash, QMap, QSet) 0108 */ 0109 bool isQtAssociativeContainer(clang::CXXRecordDecl *record); 0110 0111 /** 0112 * Overload. 0113 */ 0114 bool isQtAssociativeContainer(llvm::StringRef className); 0115 0116 /** 0117 * Returns a list of Qt containers. 0118 */ 0119 const std::vector<llvm::StringRef> & qtContainers(); 0120 0121 /** 0122 * Returns a list of implicitly shared Qt containers. 0123 */ 0124 const std::vector<llvm::StringRef> & qtCOWContainers(); 0125 0126 /** 0127 * Returns a map with the list of method names that detach each container. 0128 */ 0129 std::unordered_map<std::string, std::vector<llvm::StringRef>> detachingMethods(); 0130 0131 /** 0132 * Returns a map with the list of method names that detach each container, but only those methods 0133 * with const counterparts. 0134 */ 0135 std::unordered_map<std::string, std::vector<llvm::StringRef>> detachingMethodsWithConstCounterParts(); 0136 0137 /** 0138 * Returns true if a type represents a Qt container class. 0139 */ 0140 bool isQtContainer(clang::QualType); 0141 0142 bool isQtContainer(const clang::CXXRecordDecl *); 0143 0144 0145 /** 0146 * Returns true if -DQT_BOOTSTRAPPED was passed to the compiler 0147 */ 0148 inline bool isBootstrapping(const clang::PreprocessorOptions &ppOpts) 0149 { 0150 return clazy::isPredefined(ppOpts, "QT_BOOTSTRAPPED"); 0151 } 0152 0153 /** 0154 * Returns if decl is or derives from QObject 0155 */ 0156 bool isQObject(const clang::CXXRecordDecl *decl); 0157 0158 /** 0159 * Overload. 0160 */ 0161 bool isQObject(clang::QualType); 0162 0163 /** 0164 * Convertible means that a signal with of type source can connect to a signal/slot of type target 0165 */ 0166 bool isConvertibleTo(const clang::Type *source, const clang::Type *target); 0167 0168 /** 0169 * Returns true if \a loc is in a foreach macro 0170 */ 0171 inline bool isInForeach(const clang::ASTContext *context, clang::SourceLocation loc) 0172 { 0173 return clazy::isInAnyMacro(context, loc, { "Q_FOREACH", "foreach" }); 0174 } 0175 0176 /** 0177 * Returns true if \a record is a java-style iterator 0178 */ 0179 bool isJavaIterator(clang::CXXRecordDecl *record); 0180 0181 bool isJavaIterator(clang::CXXMemberCallExpr *call); 0182 0183 /** 0184 * Returns true if the call is on a java-style iterator class. 0185 * Returns if sizeof(T) > sizeof(void*), which would make QList<T> inefficient 0186 */ 0187 inline bool isTooBigForQList(clang::QualType qt, const clang::ASTContext *context) 0188 { 0189 return (int)context->getTypeSize(qt) <= clazy::sizeOfPointer(context, qt); 0190 } 0191 0192 /** 0193 * Returns true if a class has a ctor that has a parameter of type paramType. 0194 * ok will be false if an error occurred, or if the record is a fwd declaration, which isn't enough 0195 * for we to find out the signature. 0196 * numCtors will have the number of constructors analyized. 0197 */ 0198 bool recordHasCtorWithParam(clang::CXXRecordDecl *record, const std::string ¶mType, bool &ok, int &numCtors); 0199 0200 /** 0201 * Returns true if recordDecl is one of the container classes that supports reserve(), such 0202 * as QList, QVector, etc. 0203 */ 0204 bool isAReserveClass(clang::CXXRecordDecl *recordDecl); 0205 0206 /** 0207 * Returns the base class that inherits QObject. 0208 * Useful when the class has more than one base class and we're only interested in the QObject one. 0209 */ 0210 clang::CXXRecordDecl *getQObjectBaseClass(clang::CXXRecordDecl *recordDecl); 0211 0212 /** 0213 * Returns true if the function declaration is QObject::connect(). 0214 */ 0215 bool isConnect(clang::FunctionDecl *func); 0216 0217 /** 0218 * Returns true if the function declaration represents a QObject::connect() using the new Qt5 0219 * (pointer to member) syntax. 0220 * 0221 * It's assumed that func represents a connect(). 0222 */ 0223 bool connectHasPMFStyle(clang::FunctionDecl *func); 0224 0225 0226 /** 0227 * Returns the method referenced by a PMF-style connect for the specified connect() call. 0228 */ 0229 clang::CXXMethodDecl* pmfFromConnect(clang::CallExpr *funcCall, int argIndex); 0230 0231 clang::CXXMethodDecl* pmfFromUnary(clang::Expr *e); 0232 clang::CXXMethodDecl* pmfFromUnary(clang::UnaryOperator *uo); 0233 0234 /** 0235 * Returns the varDecl for the 1st argument in a connect call 0236 */ 0237 inline clang::ValueDecl *signalSenderForConnect(clang::CallExpr *call) 0238 { 0239 return clazy::valueDeclForCallArgument(call, 0); 0240 } 0241 0242 /** 0243 * Returns the varDecl for 3rd argument in connects that are passed an explicit 0244 * receiver or context QObject. 0245 */ 0246 inline clang::ValueDecl *signalReceiverForConnect(clang::CallExpr *call) 0247 { 0248 if (!call || call->getNumArgs() < 5) 0249 return nullptr; 0250 0251 return clazy::valueDeclForCallArgument(call, 2); 0252 } 0253 0254 /** 0255 * Returns the receiver method, in a PMF connect statement. 0256 * The method can be a slot or a signal. If it's a lambda or functor nullptr is returned 0257 */ 0258 inline clang::CXXMethodDecl* receiverMethodForConnect(clang::CallExpr *call) 0259 { 0260 0261 clang::CXXMethodDecl *receiverMethod = clazy::pmfFromConnect(call, 2); 0262 if (receiverMethod) 0263 return receiverMethod; 0264 0265 // It's either third or fourth argument 0266 return clazy::pmfFromConnect(call, 3); 0267 } 0268 0269 0270 // Returns if callExpr is a call to qobject_cast() 0271 inline bool is_qobject_cast(clang::Stmt *s, clang::CXXRecordDecl **castTo = nullptr, 0272 clang::CXXRecordDecl **castFrom = nullptr) 0273 { 0274 if (auto callExpr = llvm::dyn_cast<clang::CallExpr>(s)) { 0275 clang::FunctionDecl *func = callExpr->getDirectCallee(); 0276 if (!func || clazy::name(func) != "qobject_cast") 0277 return false; 0278 0279 if (castFrom) { 0280 clang::Expr *expr = callExpr->getArg(0); 0281 if (auto implicitCast = llvm::dyn_cast<clang::ImplicitCastExpr>(expr)) { 0282 if (implicitCast->getCastKind() == clang::CK_DerivedToBase) { 0283 expr = implicitCast->getSubExpr(); 0284 } 0285 } 0286 clang::QualType qt = clazy::pointeeQualType(expr->getType()); 0287 if (!qt.isNull()) { 0288 clang::CXXRecordDecl *record = qt->getAsCXXRecordDecl(); 0289 *castFrom = record ? record->getCanonicalDecl() : nullptr; 0290 } 0291 } 0292 0293 if (castTo) { 0294 auto templateArgs = func->getTemplateSpecializationArgs(); 0295 if (templateArgs->size() == 1) { 0296 const clang::TemplateArgument &arg = templateArgs->get(0); 0297 clang::QualType qt = clazy::pointeeQualType(arg.getAsType()); 0298 if (!qt.isNull()) { 0299 clang::CXXRecordDecl *record = qt->getAsCXXRecordDecl(); 0300 *castTo = record ? record->getCanonicalDecl() : nullptr; 0301 } 0302 } 0303 } 0304 return true; 0305 } 0306 0307 return false; 0308 } 0309 0310 inline bool isUIFile(clang::SourceLocation loc, const clang::SourceManager &sm) 0311 { 0312 const std::string filename = Utils::filenameForLoc(loc, sm); 0313 return clazy::startsWith(filename, "ui_") && clazy::endsWith(filename, ".h"); 0314 } 0315 0316 } 0317 0318 #endif