File indexing completed on 2024-05-12 05:41:03

0001 /*
0002     SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com
0003     SPDX-FileContributor: SĂ©rgio Martins <sergio.martins@kdab.com>
0004 
0005     SPDX-FileCopyrightText: 2015 Sergio Martins <smartins@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "unneeded-cast.h"
0011 #include "ClazyContext.h"
0012 #include "HierarchyUtils.h"
0013 #include "QtUtils.h"
0014 #include "TypeUtils.h"
0015 #include "Utils.h"
0016 
0017 #include <clang/AST/DeclCXX.h>
0018 #include <clang/AST/Expr.h>
0019 #include <clang/AST/ExprCXX.h>
0020 #include <clang/AST/OperationKinds.h>
0021 #include <clang/AST/Stmt.h>
0022 #include <clang/Basic/SourceLocation.h>
0023 #include <llvm/Support/Casting.h>
0024 
0025 #include <iterator>
0026 
0027 using namespace llvm;
0028 using namespace clang;
0029 
0030 // Returns if callExpr is a call to qobject_cast()
0031 inline bool is_qobject_cast(clang::Stmt *s, clang::CXXRecordDecl **castTo = nullptr, clang::CXXRecordDecl **castFrom = nullptr)
0032 {
0033     if (auto *callExpr = llvm::dyn_cast<clang::CallExpr>(s)) {
0034         clang::FunctionDecl *func = callExpr->getDirectCallee();
0035         if (!func || clazy::name(func) != "qobject_cast") {
0036             return false;
0037         }
0038 
0039         if (castFrom) {
0040             clang::Expr *expr = callExpr->getArg(0);
0041             if (auto *implicitCast = llvm::dyn_cast<clang::ImplicitCastExpr>(expr)) {
0042                 if (implicitCast->getCastKind() == clang::CK_DerivedToBase) {
0043                     expr = implicitCast->getSubExpr();
0044                 }
0045             }
0046             clang::QualType qt = clazy::pointeeQualType(expr->getType());
0047             if (!qt.isNull()) {
0048                 clang::CXXRecordDecl *record = qt->getAsCXXRecordDecl();
0049                 *castFrom = record ? record->getCanonicalDecl() : nullptr;
0050             }
0051         }
0052 
0053         if (castTo) {
0054             const auto *templateArgs = func->getTemplateSpecializationArgs();
0055             if (templateArgs->size() == 1) {
0056                 const clang::TemplateArgument &arg = templateArgs->get(0);
0057                 clang::QualType qt = clazy::pointeeQualType(arg.getAsType());
0058                 if (!qt.isNull()) {
0059                     clang::CXXRecordDecl *record = qt->getAsCXXRecordDecl();
0060                     *castTo = record ? record->getCanonicalDecl() : nullptr;
0061                 }
0062             }
0063         }
0064         return true;
0065     }
0066 
0067     return false;
0068 }
0069 
0070 UnneededCast::UnneededCast(const std::string &name, ClazyContext *context)
0071     : CheckBase(name, context, Option_CanIgnoreIncludes)
0072 {
0073 }
0074 
0075 void UnneededCast::VisitStmt(clang::Stmt *stm)
0076 {
0077     if (handleNamedCast(dyn_cast<CXXNamedCastExpr>(stm))) {
0078         return;
0079     }
0080 
0081     handleQObjectCast(stm);
0082 }
0083 
0084 bool UnneededCast::handleNamedCast(CXXNamedCastExpr *namedCast)
0085 {
0086     if (!namedCast) {
0087         return false;
0088     }
0089 
0090     const bool isDynamicCast = isa<CXXDynamicCastExpr>(namedCast);
0091     const bool isStaticCast = isDynamicCast ? false : isa<CXXStaticCastExpr>(namedCast);
0092 
0093     if (!isDynamicCast && !isStaticCast) {
0094         return false;
0095     }
0096 
0097     if (namedCast->getBeginLoc().isMacroID()) {
0098         return false;
0099     }
0100 
0101     CXXRecordDecl *castFrom = namedCast ? Utils::namedCastInnerDecl(namedCast) : nullptr;
0102     if (!castFrom || !castFrom->hasDefinition() || std::distance(castFrom->bases_begin(), castFrom->bases_end()) > 1) {
0103         return false;
0104     }
0105 
0106     if (isStaticCast) {
0107         if (auto *implicitCast = dyn_cast<ImplicitCastExpr>(namedCast->getSubExpr())) {
0108             if (implicitCast->getCastKind() == CK_NullToPointer) {
0109                 // static_cast<Foo*>(0) is OK, and sometimes needed
0110                 return false;
0111             }
0112         }
0113 
0114         // static_cast to base is needed in ternary operators
0115         if (clazy::getFirstParentOfType<ConditionalOperator>(m_context->parentMap, namedCast) != nullptr) {
0116             return false;
0117         }
0118     }
0119 
0120     if (isDynamicCast && !isOptionSet("prefer-dynamic-cast-over-qobject") && clazy::isQObject(castFrom)) {
0121         emitWarning(namedCast->getBeginLoc(), "Use qobject_cast rather than dynamic_cast");
0122     }
0123 
0124     CXXRecordDecl *castTo = Utils::namedCastOuterDecl(namedCast);
0125     if (!castTo) {
0126         return false;
0127     }
0128 
0129     return maybeWarn(namedCast, castFrom, castTo);
0130 }
0131 
0132 bool UnneededCast::handleQObjectCast(Stmt *stm)
0133 {
0134     CXXRecordDecl *castTo = nullptr;
0135     CXXRecordDecl *castFrom = nullptr;
0136 
0137     if (!is_qobject_cast(stm, &castTo, &castFrom)) {
0138         return false;
0139     }
0140 
0141     return maybeWarn(stm, castFrom, castTo, /*isQObjectCast=*/true);
0142 }
0143 
0144 bool UnneededCast::maybeWarn(Stmt *stmt, CXXRecordDecl *castFrom, CXXRecordDecl *castTo, bool isQObjectCast)
0145 {
0146     castFrom = castFrom->getCanonicalDecl();
0147     castTo = castTo->getCanonicalDecl();
0148 
0149     if (castFrom == castTo) {
0150         emitWarning(stmt->getBeginLoc(), "Casting to itself");
0151         return true;
0152     }
0153     if (clazy::derivesFrom(/*child=*/castFrom, castTo)) {
0154         if (isQObjectCast) {
0155             const bool isTernaryOperator = clazy::getFirstParentOfType<ConditionalOperator>(m_context->parentMap, stmt) != nullptr;
0156             if (isTernaryOperator) {
0157                 emitWarning(stmt->getBeginLoc(), "use static_cast instead of qobject_cast");
0158             } else {
0159                 emitWarning(stmt->getBeginLoc(), "explicitly casting to base is unnecessary");
0160             }
0161         } else {
0162             emitWarning(stmt->getBeginLoc(), "explicitly casting to base is unnecessary");
0163         }
0164 
0165         return true;
0166     }
0167 
0168     return false;
0169 }