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