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 }