diff --git a/src/TypeUtils.cpp b/src/TypeUtils.cpp index 7898779..887e649 100644 --- a/src/TypeUtils.cpp +++ b/src/TypeUtils.cpp @@ -1,166 +1,202 @@ /* This file is part of the clazy static checker. Copyright (C) 2016-2017 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. */ #include "TypeUtils.h" #include "HierarchyUtils.h" #include "StringUtils.h" #include "Utils.h" #include "StmtBodyRange.h" #include "ClazyContext.h" #include #include #include #include #include #include #include using namespace clang; bool TypeUtils::classifyQualType(const ClazyContext *context, clang::QualType qualType, const VarDecl *varDecl, QualTypeClassification &classif, clang::Stmt *body) { QualType unrefQualType = TypeUtils::unrefQualType(qualType); const Type *paramType = unrefQualType.getTypePtrOrNull(); if (!paramType || paramType->isIncompleteType()) return false; if (isUndeducibleAuto(paramType)) return false; classif.size_of_T = context->astContext.getTypeSize(unrefQualType) / 8; classif.isBig = classif.size_of_T > 16; CXXRecordDecl *recordDecl = paramType->getAsCXXRecordDecl(); CXXMethodDecl *copyCtor = recordDecl ? Utils::copyCtor(recordDecl) : nullptr; classif.isNonTriviallyCopyable = recordDecl && (recordDecl->hasNonTrivialCopyConstructor() || recordDecl->hasNonTrivialDestructor() || (copyCtor && copyCtor->isDeleted())); classif.isReference = qualType->isLValueReferenceType(); classif.isConst = unrefQualType.isConstQualified(); if (qualType->isRValueReferenceType()) // && ref, nothing to do here return true; if (classif.isConst && !classif.isReference) { classif.passNonTriviallyCopyableByConstRef = classif.isNonTriviallyCopyable; if (classif.isBig) { classif.passBigTypeByConstRef = true; } } else if (classif.isConst && classif.isReference && !classif.isNonTriviallyCopyable && !classif.isBig) { classif.passSmallTrivialByValue = true; } else if (varDecl && !classif.isConst && !classif.isReference && (classif.isBig || classif.isNonTriviallyCopyable)) { if (body && (Utils::containsNonConstMemberCall(context->parentMap, body, varDecl) || Utils::isPassedToFunction(StmtBodyRange(body), varDecl, /*byrefonly=*/ true))) return true; classif.passNonTriviallyCopyableByConstRef = classif.isNonTriviallyCopyable; if (classif.isBig) { classif.passBigTypeByConstRef = true; } } return true; } +bool TypeUtils::isSmallTrivial(const ClazyContext *context, QualType qualType) +{ + if (qualType.isNull()) + return false; + + if (qualType->isPointerType()) + qualType = qualType->getPointeeType(); + + if (qualType->isPointerType()) // We don't care about ** (We can change this whenever we have a use case) + return false; + + QualType unrefQualType = TypeUtils::unrefQualType(qualType); + const Type *paramType = unrefQualType.getTypePtrOrNull(); + if (!paramType || paramType->isIncompleteType()) + return false; + + if (isUndeducibleAuto(paramType)) + return false; + + if (qualType->isRValueReferenceType()) // && ref, nothing to do here + return false; + + CXXRecordDecl *recordDecl = paramType->getAsCXXRecordDecl(); + CXXMethodDecl *copyCtor = recordDecl ? Utils::copyCtor(recordDecl) : nullptr; + const bool hasDeletedCopyCtor = copyCtor && copyCtor->isDeleted(); + const bool isTrivial = recordDecl && !recordDecl->hasNonTrivialCopyConstructor() && !recordDecl->hasNonTrivialDestructor() && !hasDeletedCopyCtor; + + if (isTrivial) { + const auto typeSize = context->astContext.getTypeSize(unrefQualType) / 8; + const bool isSmall = typeSize <= 16; + return isSmall; + } + + return false; +} + void TypeUtils::heapOrStackAllocated(Expr *arg, const std::string &type, const clang::LangOptions &lo, bool &isStack, bool &isHeap) { isStack = false; isHeap = false; if (isa(arg)) { isHeap = true; return; } std::vector declrefs; clazy::getChilds(arg, declrefs, 3); std::vector interestingDeclRefs; for (auto declref : declrefs) { auto t = declref->getType().getTypePtrOrNull(); if (!t) continue; // Remove the '*' if it's a pointer QualType qt = t->isPointerType() ? t->getPointeeType() : declref->getType(); if (t && type == clazy::simpleTypeName(qt, lo)) { interestingDeclRefs.push_back(declref); } } if (interestingDeclRefs.size() > 1) { // Too complex return; } if (!interestingDeclRefs.empty()) { auto declref = interestingDeclRefs[0]; isStack = !declref->getType().getTypePtr()->isPointerType(); isHeap = !isStack; } } bool TypeUtils::derivesFrom(const CXXRecordDecl *derived, const CXXRecordDecl *possibleBase, std::vector *baseClasses) { if (!derived || !possibleBase || derived == possibleBase) return false; for (auto base : derived->bases()) { const Type *type = base.getType().getTypePtrOrNull(); if (!type) continue; CXXRecordDecl *baseDecl = type->getAsCXXRecordDecl(); baseDecl = baseDecl ? baseDecl->getCanonicalDecl() : nullptr; if (possibleBase == baseDecl || derivesFrom(baseDecl, possibleBase, baseClasses)) { if (baseClasses) baseClasses->push_back(baseDecl); return true; } } return false; } bool TypeUtils::derivesFrom(const clang::CXXRecordDecl *derived, const std::string &possibleBase) { if (!derived || !derived->hasDefinition()) return false; if (derived->getQualifiedNameAsString() == possibleBase) return true; for (auto base : derived->bases()) { if (derivesFrom(recordFromBaseSpecifier(base), possibleBase)) return true; } return false; } bool TypeUtils::derivesFrom(QualType derivedQT, const std::string &possibleBase) { derivedQT = pointeeQualType(derivedQT); const auto t = derivedQT.getTypePtrOrNull(); return t ? derivesFrom(t->getAsCXXRecordDecl(), possibleBase) : false; } diff --git a/src/TypeUtils.h b/src/TypeUtils.h index f1f7d6b..686283a 100644 --- a/src/TypeUtils.h +++ b/src/TypeUtils.h @@ -1,232 +1,240 @@ /* This file is part of the clazy static checker. Copyright (C) 2016-2017 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_TYPE_UTILS_H #define CLAZY_TYPE_UTILS_H #include #include #include #include #include #include #include #include #include namespace clang { class CompilerInstance; class Expr; class LangOptions; class QualType; class Stmt; class VarDecl; class Type; class CXXRecordDecl; class CXXBaseSpecifier; } class ClazyContext; namespace TypeUtils { /** * Returns the sizeof(void*) for the platform we're compiling for, in bits. */ inline int sizeOfPointer(const clang::ASTContext *context, clang::QualType qt) { if (!qt.getTypePtrOrNull()) return -1; // HACK: What's a better way of getting the size of a pointer ? return context->getTypeSize(context->getPointerType(qt)); } struct QualTypeClassification { bool isConst = false; bool isReference = false; bool isBig = false; bool isNonTriviallyCopyable = false; bool passBigTypeByConstRef = false; bool passNonTriviallyCopyableByConstRef = false; bool passSmallTrivialByValue = false; int size_of_T = 0; }; /** * Classifies a QualType, for example: * * This function is useful to know if a type should be passed by value or const-ref. * The optional parameter body is in order to advise non-const-ref -> value, since the body * needs to be inspected to see if we that would compile. */ bool classifyQualType(const ClazyContext *context, clang::QualType qualType, const clang::VarDecl *varDecl, QualTypeClassification &classification, clang::Stmt *body = nullptr); +/** + * @brief Lighter version of classifyQualType in case you just want to know if it's small and trivially copyable&destructible + * @param context The clazy context + * @param qualType The QualType we're testing. + * @return true if the type specified by QualType (or its pointee) are small and trivially copyable/destructible. + */ +bool isSmallTrivial(const ClazyContext *context, clang::QualType qualType); + /** * If qt is a reference, return it without a reference. * If qt is not a reference, return qt. * * This is useful because sometimes you have an argument like "const QString &", but qualType.isConstQualified() * returns false. Must go through qualType->getPointeeType().isConstQualified(). */ inline clang::QualType unrefQualType(clang::QualType qualType) { const clang::Type *t = qualType.getTypePtrOrNull(); return (t && t->isReferenceType()) ? t->getPointeeType() : qualType; } /** * If qt is a pointer or ref, return it without * or &. * Otherwise return qt unchanged. */ inline clang::QualType pointeeQualType(clang::QualType qualType) { // TODO: Make this recursive when we need to remove more than one level of * const clang::Type *t = qualType.getTypePtrOrNull(); return (t && (t->isReferenceType() || t->isPointerType())) ? t->getPointeeType() : qualType; } /** * Returns if @p arg is stack or heap allocated. * true means it is. false means it either isn't or the situation was too complex to judge. */ void heapOrStackAllocated(clang::Expr *arg, const std::string &type, const clang::LangOptions &lo, bool &isStack, bool &isHeap); /** * Returns true if t is an AutoType that can't be deduced. */ inline bool isUndeducibleAuto(const clang::Type *t) { if (!t) return false; auto at = llvm::dyn_cast(t); return at && at->getDeducedType().isNull(); } inline const clang::Type * unpealAuto(clang::QualType q) { if (q.isNull()) return nullptr; if (auto t = llvm::dyn_cast(q.getTypePtr())) return t->getDeducedType().getTypePtrOrNull(); return q.getTypePtr(); } /** * Returns true if childDecl is a descent from parentDecl **/ bool derivesFrom(const clang::CXXRecordDecl *derived, const clang::CXXRecordDecl *possibleBase, std::vector *baseClasses = nullptr); // Overload bool derivesFrom(const clang::CXXRecordDecl *derived, const std::string &possibleBase); // Overload bool derivesFrom(clang::QualType derived, const std::string &possibleBase); /** * Returns the CXXRecordDecl represented by the CXXBaseSpecifier */ inline clang::CXXRecordDecl * recordFromBaseSpecifier(const clang::CXXBaseSpecifier &base) { const clang::Type *t = base.getType().getTypePtrOrNull(); return t ? t->getAsCXXRecordDecl() : nullptr; } /** * Returns true if the value is const. This is usually equivalent to qt.isConstQualified() but * takes care of the special case where qt represents a pointer. Many times you don't care if the * pointer is const or not and just care about the pointee. * * A a; => false * const A a; => true * A* a; => false * const A* a; => true * A *const a; => false */ inline bool valueIsConst(clang::QualType qt) { return pointeeQualType(qt).isConstQualified(); } inline clang::CXXRecordDecl* typeAsRecord(clang::QualType qt) { if (qt.isNull()) return nullptr; return qt->getAsCXXRecordDecl(); } inline clang::CXXRecordDecl* typeAsRecord(clang::Expr *expr) { if (!expr) return nullptr; return typeAsRecord(pointeeQualType(expr->getType())); } inline clang::CXXRecordDecl* typeAsRecord(clang::ValueDecl *value) { if (!value) return nullptr; return typeAsRecord(pointeeQualType(value->getType())); } /** * Returns the class that the typedef refered by qt is in. * * class Foo { * typedef A B; * }; * * For the above example Foo would be returned. */ inline clang::CXXRecordDecl* parentRecordForTypedef(clang::QualType qt) { auto t = qt.getTypePtrOrNull(); if (!t) return nullptr; if (t->getTypeClass() == clang::Type::Typedef) { auto tdt = static_cast(t); clang::TypedefNameDecl *tdnd = tdt->getDecl(); return llvm::dyn_cast_or_null(tdnd->getDeclContext()); } return nullptr; } // Example: const QString & inline bool isConstRef(const clang::Type *t) { return t && t->isReferenceType() && t->getPointeeType().isConstQualified(); } } #endif