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 }