File indexing completed on 2024-04-28 16:57:53

0001 /*
0002     This file is part of the clazy static checker.
0003 
0004     Copyright (C) 2016-2017 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 "TypeUtils.h"
0023 #include "HierarchyUtils.h"
0024 #include "StringUtils.h"
0025 #include "Utils.h"
0026 #include "StmtBodyRange.h"
0027 #include "ClazyContext.h"
0028 
0029 #include <clang/AST/ASTContext.h>
0030 #include <clang/AST/DeclCXX.h>
0031 #include <clang/AST/Expr.h>
0032 #include <clang/AST/ExprCXX.h>
0033 #include <clang/AST/Stmt.h>
0034 #include <clang/AST/Type.h>
0035 #include <clang/Basic/LLVM.h>
0036 
0037 using namespace clang;
0038 
0039 bool clazy::classifyQualType(const ClazyContext *context, clang::QualType qualType,
0040                              const VarDecl *varDecl, QualTypeClassification &classif,
0041                              clang::Stmt *body)
0042 {
0043     QualType unrefQualType = clazy::unrefQualType(qualType);
0044     const Type *paramType = unrefQualType.getTypePtrOrNull();
0045     if (!paramType || paramType->isIncompleteType())
0046         return false;
0047 
0048     if (isUndeducibleAuto(paramType))
0049         return false;
0050 
0051     classif.size_of_T = context->astContext.getTypeSize(unrefQualType) / 8;
0052     classif.isBig = classif.size_of_T > 16;
0053     CXXRecordDecl *recordDecl = paramType->getAsCXXRecordDecl();
0054     CXXMethodDecl *copyCtor = recordDecl ? Utils::copyCtor(recordDecl) : nullptr;
0055     classif.isNonTriviallyCopyable = recordDecl && (recordDecl->hasNonTrivialCopyConstructor() || recordDecl->hasNonTrivialDestructor() || (copyCtor && copyCtor->isDeleted()));
0056     classif.isReference = qualType->isLValueReferenceType();
0057     classif.isConst = unrefQualType.isConstQualified();
0058 
0059     if (qualType->isRValueReferenceType()) // && ref, nothing to do here
0060         return true;
0061 
0062     if (classif.isConst && !classif.isReference) {
0063         classif.passNonTriviallyCopyableByConstRef = classif.isNonTriviallyCopyable;
0064         if (classif.isBig) {
0065             classif.passBigTypeByConstRef = true;
0066         }
0067     } else if (classif.isConst && classif.isReference && !classif.isNonTriviallyCopyable && !classif.isBig) {
0068         classif.passSmallTrivialByValue = true;
0069     } else if (varDecl && !classif.isConst && !classif.isReference && (classif.isBig || classif.isNonTriviallyCopyable)) {
0070         if (body && (Utils::containsNonConstMemberCall(context->parentMap, body, varDecl) || Utils::isPassedToFunction(StmtBodyRange(body), varDecl, /*byrefonly=*/ true)))
0071             return true;
0072 
0073         classif.passNonTriviallyCopyableByConstRef = classif.isNonTriviallyCopyable;
0074         if (classif.isBig) {
0075             classif.passBigTypeByConstRef = true;
0076         }
0077     }
0078 
0079     return true;
0080 }
0081 
0082 bool clazy::isSmallTrivial(const ClazyContext *context, QualType qualType)
0083 {
0084     if (qualType.isNull())
0085         return false;
0086 
0087     if (qualType->isPointerType())
0088         qualType = qualType->getPointeeType();
0089 
0090     if (qualType->isPointerType()) // We don't care about ** (We can change this whenever we have a use case)
0091         return false;
0092 
0093     QualType unrefQualType = clazy::unrefQualType(qualType);
0094     const Type *paramType = unrefQualType.getTypePtrOrNull();
0095     if (!paramType || paramType->isIncompleteType())
0096         return false;
0097 
0098     if (isUndeducibleAuto(paramType))
0099         return false;
0100 
0101     if (qualType->isRValueReferenceType()) // && ref, nothing to do here
0102         return false;
0103 
0104      CXXRecordDecl *recordDecl = paramType->getAsCXXRecordDecl();
0105      CXXMethodDecl *copyCtor = recordDecl ? Utils::copyCtor(recordDecl) : nullptr;
0106      const bool hasDeletedCopyCtor = copyCtor && copyCtor->isDeleted();
0107      const bool isTrivial = recordDecl && !recordDecl->hasNonTrivialCopyConstructor() && !recordDecl->hasNonTrivialDestructor() && !hasDeletedCopyCtor;
0108 
0109      if (isTrivial) {
0110          const auto typeSize = context->astContext.getTypeSize(unrefQualType) / 8;
0111          const bool isSmall = typeSize <= 16;
0112          return isSmall;
0113      }
0114 
0115      return false;
0116 }
0117 
0118 void clazy::heapOrStackAllocated(Expr *arg, const std::string &type,
0119                                      const clang::LangOptions &lo,
0120                                      bool &isStack, bool &isHeap)
0121 {
0122     isStack = false;
0123     isHeap = false;
0124     if (isa<CXXNewExpr>(arg)) {
0125         isHeap = true;
0126         return;
0127     }
0128 
0129     std::vector<DeclRefExpr*> declrefs;
0130     clazy::getChilds(arg, declrefs, 3);
0131 
0132     std::vector<DeclRefExpr*> interestingDeclRefs;
0133     for (auto declref : declrefs) {
0134         auto t = declref->getType().getTypePtrOrNull();
0135         if (!t)
0136             continue;
0137 
0138         // Remove the '*' if it's a pointer
0139         QualType qt = t->isPointerType() ? t->getPointeeType()
0140                                          : declref->getType();
0141 
0142         if (t && type == clazy::simpleTypeName(qt, lo)) {
0143             interestingDeclRefs.push_back(declref);
0144         }
0145     }
0146 
0147     if (interestingDeclRefs.size() > 1) {
0148         // Too complex
0149         return;
0150     }
0151 
0152     if (!interestingDeclRefs.empty()) {
0153         auto declref = interestingDeclRefs[0];
0154         isStack = !declref->getType().getTypePtr()->isPointerType();
0155         isHeap = !isStack;
0156     }
0157 }
0158 
0159 bool clazy::derivesFrom(const CXXRecordDecl *derived, const CXXRecordDecl *possibleBase,
0160                             std::vector<CXXRecordDecl*> *baseClasses)
0161 {
0162     if (!derived || !possibleBase || derived == possibleBase)
0163         return false;
0164 
0165     for (auto base : derived->bases()) {
0166         const Type *type = base.getType().getTypePtrOrNull();
0167         if (!type) continue;
0168         CXXRecordDecl *baseDecl = type->getAsCXXRecordDecl();
0169         baseDecl = baseDecl ? baseDecl->getCanonicalDecl() : nullptr;
0170 
0171         if (possibleBase == baseDecl || derivesFrom(baseDecl, possibleBase, baseClasses)) {
0172             if (baseClasses)
0173                 baseClasses->push_back(baseDecl);
0174             return true;
0175         }
0176     }
0177 
0178     return false;
0179 }
0180 
0181 bool clazy::derivesFrom(const clang::CXXRecordDecl *derived, const std::string &possibleBase)
0182 {
0183     if (!derived || !derived->hasDefinition())
0184         return false;
0185 
0186     if (derived->getQualifiedNameAsString() == possibleBase)
0187         return true;
0188 
0189     for (auto base : derived->bases()) {
0190         if (derivesFrom(recordFromBaseSpecifier(base), possibleBase))
0191             return true;
0192     }
0193 
0194     return false;
0195 }
0196 
0197 bool clazy::derivesFrom(QualType derivedQT, const std::string &possibleBase)
0198 {
0199     derivedQT = pointeeQualType(derivedQT);
0200     const auto t = derivedQT.getTypePtrOrNull();
0201     return t ? derivesFrom(t->getAsCXXRecordDecl(), possibleBase) : false;
0202 }