File indexing completed on 2024-04-21 05:38:49

0001 /*
0002     SPDX-FileCopyrightText: 2016-2017 Sergio Martins <smartins@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef CLAZY_TYPE_UTILS_H
0008 #define CLAZY_TYPE_UTILS_H
0009 
0010 #include <clang/AST/ASTContext.h>
0011 #include <clang/AST/Decl.h>
0012 #include <clang/AST/DeclCXX.h>
0013 #include <clang/AST/Expr.h>
0014 #include <clang/AST/StmtCXX.h>
0015 #include <clang/AST/Type.h>
0016 #include <llvm/Support/Casting.h>
0017 
0018 #include <string>
0019 #include <vector>
0020 
0021 namespace clang
0022 {
0023 class CompilerInstance;
0024 class Expr;
0025 class LangOptions;
0026 class QualType;
0027 class Stmt;
0028 class VarDecl;
0029 class Type;
0030 class CXXRecordDecl;
0031 class CXXBaseSpecifier;
0032 }
0033 
0034 class ClazyContext;
0035 
0036 namespace clazy
0037 {
0038 /**
0039  * Returns the sizeof(void*) for the platform we're compiling for, in bits.
0040  */
0041 inline int sizeOfPointer(const clang::ASTContext *context, clang::QualType qt)
0042 {
0043     if (!qt.getTypePtrOrNull()) {
0044         return -1;
0045     }
0046     // HACK: What's a better way of getting the size of a pointer ?
0047     return context->getTypeSize(context->getPointerType(qt));
0048 }
0049 
0050 struct QualTypeClassification {
0051     bool isConst = false;
0052     bool isReference = false;
0053     bool isBig = false;
0054     bool isNonTriviallyCopyable = false;
0055     bool passBigTypeByConstRef = false;
0056     bool passNonTriviallyCopyableByConstRef = false;
0057     bool passSmallTrivialByValue = false;
0058     int size_of_T = 0;
0059 };
0060 
0061 /**
0062  * Classifies a QualType, for example:
0063  *
0064  * This function is useful to know if a type should be passed by value or const-ref.
0065  * The optional parameter body is in order to advise non-const-ref -> value, since the body
0066  * needs to be inspected to see if we that would compile.
0067  */
0068 bool classifyQualType(const ClazyContext *context,
0069                       clang::QualType qualType,
0070                       const clang::VarDecl *varDecl,
0071                       QualTypeClassification &classification,
0072                       clang::Stmt *body = nullptr);
0073 
0074 /**
0075  * @brief Lighter version of classifyQualType in case you just want to know if it's small and trivially copyable&destructible
0076  * @param context The clazy context
0077  * @param qualType The QualType we're testing.
0078  * @return true if the type specified by QualType (or its pointee) are small and trivially copyable/destructible.
0079  */
0080 bool isSmallTrivial(const ClazyContext *context, clang::QualType qualType);
0081 
0082 /**
0083  * If qt is a reference, return it without a reference.
0084  * If qt is not a reference, return qt.
0085  *
0086  * This is useful because sometimes you have an argument like "const QString &", but qualType.isConstQualified()
0087  * returns false. Must go through qualType->getPointeeType().isConstQualified().
0088  */
0089 inline clang::QualType unrefQualType(clang::QualType qualType)
0090 {
0091     const clang::Type *t = qualType.getTypePtrOrNull();
0092     return (t && t->isReferenceType()) ? t->getPointeeType() : qualType;
0093 }
0094 
0095 /**
0096  * If qt is a pointer or ref, return it without * or &.
0097  * Otherwise return qt unchanged.
0098  */
0099 inline clang::QualType pointeeQualType(clang::QualType qualType)
0100 {
0101     // TODO: Make this recursive when we need to remove more than one level of *
0102     const clang::Type *t = qualType.getTypePtrOrNull();
0103     return (t && (t->isReferenceType() || t->isPointerType())) ? t->getPointeeType() : qualType;
0104 }
0105 
0106 /**
0107  * Returns if @p arg is stack or heap allocated.
0108  * true means it is. false means it either isn't or the situation was too complex to judge.
0109  */
0110 void heapOrStackAllocated(clang::Expr *arg, const std::string &type, const clang::LangOptions &lo, bool &isStack, bool &isHeap);
0111 
0112 /**
0113  * Returns true if t is an AutoType that can't be deduced.
0114  */
0115 inline bool isUndeducibleAuto(const clang::Type *t)
0116 {
0117     if (!t) {
0118         return false;
0119     }
0120 
0121     const auto *at = llvm::dyn_cast<clang::AutoType>(t);
0122     return at && at->getDeducedType().isNull();
0123 }
0124 
0125 inline const clang::Type *unpealAuto(clang::QualType q)
0126 {
0127     if (q.isNull()) {
0128         return nullptr;
0129     }
0130 
0131     if (const auto *t = llvm::dyn_cast<clang::AutoType>(q.getTypePtr())) {
0132         return t->getDeducedType().getTypePtrOrNull();
0133     }
0134 
0135     return q.getTypePtr();
0136 }
0137 
0138 /**
0139  * Returns true if childDecl is a descent from parentDecl
0140  **/
0141 bool derivesFrom(const clang::CXXRecordDecl *derived, const clang::CXXRecordDecl *possibleBase, std::vector<clang::CXXRecordDecl *> *baseClasses = nullptr);
0142 
0143 // Overload
0144 bool derivesFrom(const clang::CXXRecordDecl *derived, const std::string &possibleBase);
0145 
0146 // Overload
0147 bool derivesFrom(clang::QualType derived, const std::string &possibleBase);
0148 
0149 /**
0150  * Returns the CXXRecordDecl represented by the CXXBaseSpecifier
0151  */
0152 inline clang::CXXRecordDecl *recordFromBaseSpecifier(const clang::CXXBaseSpecifier &base)
0153 {
0154     const clang::Type *t = base.getType().getTypePtrOrNull();
0155     return t ? t->getAsCXXRecordDecl() : nullptr;
0156 }
0157 /**
0158  * Returns true if the value is const. This is usually equivalent to qt.isConstQualified() but
0159  * takes care of the special case where qt represents a pointer. Many times you don't care if the
0160  * pointer is const or not and just care about the pointee.
0161  *
0162  * A a;        => false
0163  * const A a;  => true
0164  * A* a;       => false
0165  * const A* a; => true
0166  * A *const a; => false
0167  */
0168 inline bool valueIsConst(clang::QualType qt)
0169 {
0170     return pointeeQualType(qt).isConstQualified();
0171 }
0172 
0173 inline clang::CXXRecordDecl *typeAsRecord(clang::QualType qt)
0174 {
0175     if (qt.isNull()) {
0176         return nullptr;
0177     }
0178 
0179     return qt->getAsCXXRecordDecl();
0180 }
0181 
0182 inline clang::CXXRecordDecl *typeAsRecord(clang::Expr *expr)
0183 {
0184     if (!expr) {
0185         return nullptr;
0186     }
0187 
0188     return typeAsRecord(pointeeQualType(expr->getType()));
0189 }
0190 
0191 inline clang::CXXRecordDecl *typeAsRecord(clang::ValueDecl *value)
0192 {
0193     if (!value) {
0194         return nullptr;
0195     }
0196 
0197     return typeAsRecord(pointeeQualType(value->getType()));
0198 }
0199 
0200 /**
0201  * Returns the class that the typedef refered by qt is in.
0202  *
0203  * class Foo {
0204  *     typedef A B;
0205  * };
0206  *
0207  * For the above example Foo would be returned.
0208  */
0209 inline clang::CXXRecordDecl *parentRecordForTypedef(clang::QualType qt)
0210 {
0211     auto *t = qt.getTypePtrOrNull();
0212 
0213     if (const auto *elab = llvm::dyn_cast<clang::ElaboratedType>(t)) {
0214         t = elab->desugar().getTypePtrOrNull();
0215     }
0216 
0217     if (const auto *tdt = llvm::dyn_cast<clang::TypedefType>(t)) {
0218         clang::TypedefNameDecl *tdnd = tdt->getDecl();
0219         return llvm::dyn_cast_or_null<clang::CXXRecordDecl>(tdnd->getDeclContext());
0220     }
0221 
0222     return nullptr;
0223 }
0224 
0225 // Example: const QString &
0226 inline bool isConstRef(const clang::Type *t)
0227 {
0228     return t && t->isReferenceType() && t->getPointeeType().isConstQualified();
0229 }
0230 }
0231 
0232 #endif