File indexing completed on 2024-05-12 05:41:00
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 "implicit-casts.h" 0011 #include "ClazyContext.h" 0012 #include "HierarchyUtils.h" 0013 #include "Utils.h" 0014 #include "clazy_stl.h" 0015 0016 #include <clang/AST/Decl.h> 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/AST/Type.h> 0023 #include <clang/Basic/LLVM.h> 0024 #include <clang/Basic/Linkage.h> 0025 #include <clang/Basic/SourceLocation.h> 0026 #include <clang/Lex/Lexer.h> 0027 #include <llvm/ADT/ArrayRef.h> 0028 #include <llvm/ADT/StringRef.h> 0029 #include <llvm/Support/Casting.h> 0030 0031 #include <vector> 0032 0033 namespace clang 0034 { 0035 class ParentMap; 0036 } // namespace clang 0037 0038 using namespace clang; 0039 0040 ImplicitCasts::ImplicitCasts(const std::string &name, ClazyContext *context) 0041 : CheckBase(name, context, Option_CanIgnoreIncludes) 0042 { 0043 m_filesToIgnore = {"qobject_impl.h", "qdebug.h", "hb-", "qdbusintegrator.cpp", "harfbuzz-", "qunicodetools.cpp"}; 0044 } 0045 0046 static bool isInterestingFunction(FunctionDecl *func) 0047 { 0048 if (!func) { 0049 return false; 0050 } 0051 0052 // The interesting function calls for the pointertoBool check are those having bool and also pointer arguments, 0053 // which might get mixed 0054 0055 bool hasBoolArgument = false; 0056 bool hasPointerArgument = false; 0057 0058 for (auto *param : Utils::functionParameters(func)) { 0059 const Type *t = param->getType().getTypePtrOrNull(); 0060 hasBoolArgument |= (t && t->isBooleanType()); 0061 hasPointerArgument |= (t && t->isPointerType()); 0062 0063 if (hasBoolArgument && hasPointerArgument) { 0064 return true; 0065 } 0066 } 0067 0068 return false; 0069 } 0070 0071 // Checks for pointer->bool implicit casts 0072 template<typename T> 0073 static bool iterateCallExpr(T *callExpr, CheckBase *check) 0074 { 0075 if (!callExpr) { 0076 return false; 0077 } 0078 0079 bool result = false; 0080 0081 int i = 0; 0082 for (auto arg : callExpr->arguments()) { 0083 ++i; 0084 auto implicitCast = dyn_cast<ImplicitCastExpr>(arg); 0085 if (!implicitCast || implicitCast->getCastKind() != clang::CK_PointerToBoolean) { 0086 continue; 0087 } 0088 0089 check->emitWarning(implicitCast->getBeginLoc(), "Implicit pointer to bool cast (argument " + std::to_string(i) + ')'); 0090 result = true; 0091 } 0092 0093 return result; 0094 } 0095 0096 // Checks for bool->int implicit casts 0097 template<typename T> 0098 static bool iterateCallExpr2(T *callExpr, CheckBase *check, ParentMap *parentMap) 0099 { 0100 if (!callExpr) { 0101 return false; 0102 } 0103 0104 bool result = false; 0105 0106 int i = 0; 0107 for (auto arg : callExpr->arguments()) { 0108 ++i; 0109 auto implicitCast = dyn_cast<ImplicitCastExpr>(arg); 0110 if (!implicitCast || implicitCast->getCastKind() != clang::CK_IntegralCast) { 0111 continue; 0112 } 0113 0114 if (implicitCast->getType().getTypePtrOrNull()->isBooleanType()) { 0115 continue; 0116 } 0117 0118 Expr *expr = implicitCast->getSubExpr(); 0119 QualType qt = expr->getType(); 0120 0121 if (!qt.getTypePtrOrNull()->isBooleanType()) { // Filter out some bool to const bool 0122 continue; 0123 } 0124 0125 if (clazy::getFirstChildOfType<CXXFunctionalCastExpr>(implicitCast)) { 0126 continue; 0127 } 0128 0129 if (clazy::getFirstChildOfType<CStyleCastExpr>(implicitCast)) { 0130 continue; 0131 } 0132 0133 if (Utils::isInsideOperatorCall(parentMap, implicitCast, {"QTextStream", "QAtomicInt", "QBasicAtomicInt"})) { 0134 continue; 0135 } 0136 0137 if (Utils::insideCTORCall(parentMap, implicitCast, {"QAtomicInt", "QBasicAtomicInt"})) { 0138 continue; 0139 } 0140 0141 check->emitWarning(implicitCast->getBeginLoc(), "Implicit bool to int cast (argument " + std::to_string(i) + ')'); 0142 result = true; 0143 } 0144 0145 return result; 0146 } 0147 0148 void ImplicitCasts::VisitStmt(clang::Stmt *stmt) 0149 { 0150 // Lets check only in function calls. Otherwise there are too many false positives, it's common 0151 // to implicit cast to bool when checking pointers for validity, like if (ptr) 0152 0153 auto *callExpr = dyn_cast<CallExpr>(stmt); 0154 CXXConstructExpr *ctorExpr = nullptr; 0155 if (!callExpr) { 0156 ctorExpr = dyn_cast<CXXConstructExpr>(stmt); 0157 if (!ctorExpr) { 0158 return; 0159 } 0160 } 0161 0162 if (isa<CXXOperatorCallExpr>(stmt)) { 0163 return; 0164 } 0165 0166 if (isMacroToIgnore(stmt->getBeginLoc())) { 0167 return; 0168 } 0169 0170 if (shouldIgnoreFile(stmt->getBeginLoc())) { 0171 return; 0172 } 0173 0174 FunctionDecl *func = callExpr ? callExpr->getDirectCallee() : ctorExpr->getConstructor(); 0175 0176 if (isInterestingFunction(func)) { 0177 // Check pointer->bool implicit casts 0178 iterateCallExpr<CallExpr>(callExpr, this); 0179 iterateCallExpr<CXXConstructExpr>(ctorExpr, this); 0180 } else if (isBoolToInt(func)) { 0181 // Check bool->int implicit casts 0182 iterateCallExpr2<CallExpr>(callExpr, this, m_context->parentMap); 0183 iterateCallExpr2<CXXConstructExpr>(ctorExpr, this, m_context->parentMap); 0184 } 0185 } 0186 0187 bool ImplicitCasts::isBoolToInt(FunctionDecl *func) const 0188 { 0189 if (!func || !isOptionSet("bool-to-int")) { 0190 return false; 0191 } 0192 0193 if (func->getLanguageLinkage() != CXXLanguageLinkage || func->isVariadic()) { 0194 return false; // Disabled for now, too many false-positives when interacting with C code 0195 } 0196 0197 static const std::vector<std::string> functions = {"QString::arg"}; 0198 return !clazy::contains(functions, func->getQualifiedNameAsString()); 0199 } 0200 0201 bool ImplicitCasts::isMacroToIgnore(SourceLocation loc) const 0202 { 0203 static const std::vector<StringRef> macros = {"QVERIFY", "Q_UNLIKELY", "Q_LIKELY"}; 0204 if (!loc.isMacroID()) { 0205 return false; 0206 } 0207 StringRef macro = Lexer::getImmediateMacroName(loc, sm(), lo()); 0208 return clazy::contains(macros, macro); 0209 }