/* This file is part of the clazy static checker. Copyright (C) 2015-2016 Sergio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CLAZY_QT_UTILS_H #define CLAZY_QT_UTILS_H #include "clazy_export.h" #include "TypeUtils.h" #include "MacroUtils.h" #include "FunctionUtils.h" #include #include #include #include namespace clang { class CXXRecordDecl; class CompilerInstance; class Type; class CXXMemberCallExpr; class CallExpr; class ValueDecl; class LangOptions; class QualType; class VarDecl; class SourceLocation; class FunctionDecl; class UnaryOperator; class CXXMethodDecl; class Expr; } struct StmtBodyRange; namespace clazy { /** * Returns true if the class is a Qt class which can be iterated with foreach. * Which means all containers and also stuff like QAssociativeIterable. */ CLAZYLIB_EXPORT bool isQtIterableClass(clang::CXXRecordDecl *record); /** * Overload. */ CLAZYLIB_EXPORT bool isQtIterableClass(llvm::StringRef className); /** * Returns true if the class is a Qt class which can be iterated with foreach and also implicitly shared. */ CLAZYLIB_EXPORT bool isQtCOWIterableClass(clang::CXXRecordDecl *record); /** * Overload. */ CLAZYLIB_EXPORT bool isQtCOWIterableClass(const std::string &className); /** * Returns if the iterators belongs to a COW container */ inline bool isQtCOWIterator(clang::CXXRecordDecl *itRecord) { if (!itRecord) return false; auto parent = llvm::dyn_cast_or_null(itRecord->getParent()); return parent && clazy::isQtCOWIterableClass(parent); } /** * Returns true if the class is a Qt class which is an associative container (QHash, QMap, QSet) */ CLAZYLIB_EXPORT bool isQtAssociativeContainer(clang::CXXRecordDecl *record); /** * Overload. */ CLAZYLIB_EXPORT bool isQtAssociativeContainer(llvm::StringRef className); /** * Returns a list of Qt containers. */ CLAZYLIB_EXPORT const std::vector & qtContainers(); /** * Returns a list of implicitly shared Qt containers. */ CLAZYLIB_EXPORT const std::vector & qtCOWContainers(); /** * Returns a map with the list of method names that detach each container. */ CLAZYLIB_EXPORT std::unordered_map > detachingMethods(); /** * Returns a map with the list of method names that detach each container, but only those methods * with const counterparts. */ CLAZYLIB_EXPORT std::unordered_map > detachingMethodsWithConstCounterParts(); /** * Returns true if a type represents a Qt container class. */ CLAZYLIB_EXPORT bool isQtContainer(clang::QualType); CLAZYLIB_EXPORT bool isQtContainer(const clang::CXXRecordDecl *); /** * Returns true if -DQT_BOOTSTRAPPED was passed to the compiler */ inline bool isBootstrapping(const clang::PreprocessorOptions &ppOpts) { return clazy::isPredefined(ppOpts, "QT_BOOTSTRAPPED"); } /** * Returns if decl is or derives from QObject */ CLAZYLIB_EXPORT bool isQObject(const clang::CXXRecordDecl *decl); /** * Overload. */ CLAZYLIB_EXPORT bool isQObject(clang::QualType); /** * Convertible means that a signal with of type source can connect to a signal/slot of type target */ CLAZYLIB_EXPORT bool isConvertibleTo(const clang::Type *source, const clang::Type *target); /** * Returns true if \a loc is in a foreach macro */ inline bool isInForeach(const clang::ASTContext *context, clang::SourceLocation loc) { return clazy::isInAnyMacro(context, loc, { "Q_FOREACH", "foreach" }); } /** * Returns true if \a record is a java-style iterator */ CLAZYLIB_EXPORT bool isJavaIterator(clang::CXXRecordDecl *record); CLAZYLIB_EXPORT bool isJavaIterator(clang::CXXMemberCallExpr *call); /** * Returns true if the call is on a java-style iterator class. * Returns if sizeof(T) > sizeof(void*), which would make QList inefficient */ inline bool isTooBigForQList(clang::QualType qt, const clang::ASTContext *context) { return (int)context->getTypeSize(qt) <= TypeUtils::sizeOfPointer(context, qt); } /** * Returns true if a class has a ctor that has a parameter of type paramType. * ok will be false if an error occurred, or if the record is a fwd declaration, which isn't enough * for we to find out the signature. * numCtors will have the number of constructors analyized. */ bool recordHasCtorWithParam(clang::CXXRecordDecl *record, const std::string ¶mType, bool &ok, int &numCtors); /** * Returns true if we can prove the container doesn't detach. * Returns false otherwise, meaning that you can't conclude anything if false is returned. * * For true to be returned, all these conditions must verify: * - Container is a local variable * - It's not passed to any function * - It's not assigned to another variable */ CLAZYLIB_EXPORT bool containerNeverDetaches(const clang::VarDecl *varDecl, StmtBodyRange bodyRange); /** * Returns true if recordDecl is one of the container classes that supports reserve(), such * as QList, QVector, etc. */ CLAZYLIB_EXPORT bool isAReserveClass(clang::CXXRecordDecl *recordDecl); /** * Returns the base class that inherits QObject. * Useful when the class has more than one base class and we're only interested in the QObject one. */ CLAZYLIB_EXPORT clang::CXXRecordDecl *getQObjectBaseClass(clang::CXXRecordDecl *recordDecl); /** * Returns true if the function declaration is QObject::connect(). */ CLAZYLIB_EXPORT bool isConnect(clang::FunctionDecl *func); /** * Returns true if the function declaration represents a QObject::connect() using the new Qt5 * (pointer to member) syntax. * * It's assumed that func represents a connect(). */ CLAZYLIB_EXPORT bool connectHasPMFStyle(clang::FunctionDecl *func); /** * Returns the method referenced by a PMF-style connect for the specified connect() call. */ CLAZYLIB_EXPORT clang::CXXMethodDecl* pmfFromConnect(clang::CallExpr *funcCall, int argIndex); CLAZYLIB_EXPORT clang::CXXMethodDecl* pmfFromUnary(clang::Expr *e); CLAZYLIB_EXPORT clang::CXXMethodDecl* pmfFromUnary(clang::UnaryOperator *uo); /** * Returns the varDecl for the 1st argument in a connect call */ inline clang::ValueDecl *signalSenderForConnect(clang::CallExpr *call) { return clazy::valueDeclForCallArgument(call, 0); } /** * Returns the varDecl for 3rd argument in connects that are passed an explicit * receiver or context QObject. */ inline clang::ValueDecl *signalReceiverForConnect(clang::CallExpr *call) { if (!call || call->getNumArgs() < 5) return nullptr; return clazy::valueDeclForCallArgument(call, 3); } /** * Returns the receiver method, in a PMF connect statement. * The method can be a slot or a signal. If it's a lambda or functor nullptr is returned */ inline clang::CXXMethodDecl* receiverMethodForConnect(clang::CallExpr *call) { clang::CXXMethodDecl *receiverMethod = clazy::pmfFromConnect(call, 2); if (receiverMethod) return receiverMethod; // It's either third or fourth argument return clazy::pmfFromConnect(call, 3); } // Returns if callExpr is a call to qobject_cast() inline bool is_qobject_cast(clang::Stmt *s, clang::CXXRecordDecl **castTo = nullptr, clang::CXXRecordDecl **castFrom = nullptr) { if (auto callExpr = llvm::dyn_cast(s)) { clang::FunctionDecl *func = callExpr->getDirectCallee(); if (!func || clazy::name(func) != "qobject_cast") return false; if (castFrom) { clang::Expr *expr = callExpr->getArg(0); if (auto implicitCast = llvm::dyn_cast(expr)) { if (implicitCast->getCastKind() == clang::CK_DerivedToBase) { expr = implicitCast->getSubExpr(); } } clang::QualType qt = TypeUtils::pointeeQualType(expr->getType()); if (!qt.isNull()) { clang::CXXRecordDecl *record = qt->getAsCXXRecordDecl(); *castFrom = record ? record->getCanonicalDecl() : nullptr; } } if (castTo) { auto templateArgs = func->getTemplateSpecializationArgs(); if (templateArgs->size() == 1) { const clang::TemplateArgument &arg = templateArgs->get(0); clang::QualType qt = TypeUtils::pointeeQualType(arg.getAsType()); if (!qt.isNull()) { clang::CXXRecordDecl *record = qt->getAsCXXRecordDecl(); *castTo = record ? record->getCanonicalDecl() : nullptr; } } } return true; } return false; } } #endif