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

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 "assert-with-side-effects.h"
0011 #include "MacroUtils.h"
0012 #include "StringUtils.h"
0013 #include "clazy_stl.h"
0014 
0015 #include <clang/AST/Decl.h>
0016 #include <clang/AST/DeclCXX.h>
0017 #include <clang/AST/Expr.h>
0018 #include <clang/AST/ExprCXX.h>
0019 #include <clang/AST/OperationKinds.h>
0020 #include <clang/AST/Stmt.h>
0021 #include <clang/Basic/LLVM.h>
0022 #include <clang/Basic/SourceLocation.h>
0023 #include <clang/Basic/SourceManager.h>
0024 #include <llvm/ADT/StringRef.h>
0025 #include <llvm/Support/Casting.h>
0026 
0027 #include <vector>
0028 
0029 class ClazyContext;
0030 
0031 using namespace clang;
0032 
0033 enum Aggressiveness {
0034     NormalAggressiveness = 0,
0035     AlsoCheckFunctionCallsAggressiveness = 1 // too many false positives
0036 };
0037 
0038 AssertWithSideEffects::AssertWithSideEffects(const std::string &name, ClazyContext *context)
0039     : CheckBase(name, context, Option_CanIgnoreIncludes)
0040     , m_aggressiveness(NormalAggressiveness)
0041 {
0042 }
0043 
0044 static bool functionIsOk(StringRef name)
0045 {
0046     static const std::vector<StringRef> whitelist = {"qFuzzyIsNull", "qt_noop",   "qt_assert",    "qIsFinite",     "qIsInf",     "qIsNaN",     "qIsNumericType",
0047                                                      "operator==",   "operator<", "operator>",    "operator<=",    "operator>=", "operator!=", "operator+",
0048                                                      "operator-",    "q_func",    "d_func",       "isEmptyHelper", "qCross",     "qMin",       "qMax",
0049                                                      "qBound",       "priv",      "qobject_cast", "dbusService"};
0050     return clazy::contains(whitelist, name);
0051 }
0052 
0053 static bool methodIsOK(const std::string &name)
0054 {
0055     static const std::vector<std::string> whitelist = {
0056         "QList::begin",
0057         "QList::end",
0058         "QVector::begin",
0059         "QVector::end",
0060         "QHash::begin",
0061         "QHash::end",
0062         "QByteArray::data",
0063         "QBasicMutex::isRecursive",
0064         "QLinkedList::begin",
0065         "QLinkedList::end",
0066         "QDataBuffer::first",
0067         "QOpenGLFunctions::glIsRenderbuffer",
0068     };
0069     return clazy::contains(whitelist, name);
0070 }
0071 
0072 void AssertWithSideEffects::VisitStmt(Stmt *stm)
0073 {
0074     const SourceLocation stmStart = stm->getBeginLoc();
0075     if (!clazy::isInMacro(&m_astContext, stmStart, "Q_ASSERT")) {
0076         return;
0077     }
0078 
0079     bool warn = false;
0080     const bool checkfunctions = m_aggressiveness & AlsoCheckFunctionCallsAggressiveness;
0081 
0082     auto *memberCall = dyn_cast<CXXMemberCallExpr>(stm);
0083     if (memberCall) {
0084         if (checkfunctions) {
0085             CXXMethodDecl *method = memberCall->getMethodDecl();
0086             if (!method->isConst() && !methodIsOK(clazy::qualifiedMethodName(method)) && !functionIsOk(clazy::name(method))) {
0087                 // llvm::errs() << "reason1 " << clazy::qualifiedMethodName(method) << "\n";
0088                 warn = true;
0089             }
0090         }
0091     } else if (auto *call = dyn_cast<CallExpr>(stm)) {
0092         // Non member function calls not allowed
0093 
0094         FunctionDecl *func = call->getDirectCallee();
0095         if (func && checkfunctions) {
0096             if (isa<CXXMethodDecl>(func)) { // This will be visited next, so ignore it now
0097                 return;
0098             }
0099 
0100             if (functionIsOk(clazy::name(func))) {
0101                 return;
0102             }
0103 
0104             warn = true;
0105         }
0106     } else if (auto *op = dyn_cast<BinaryOperator>(stm)) {
0107         if (op->isAssignmentOp()) {
0108             if (auto *declRef = dyn_cast<DeclRefExpr>(op->getLHS())) {
0109                 ValueDecl *valueDecl = declRef->getDecl();
0110                 if (valueDecl && sm().isBeforeInSLocAddrSpace(valueDecl->getBeginLoc(), stmStart)) {
0111                     // llvm::errs() << "reason3\n";
0112                     warn = true;
0113                 }
0114             }
0115         }
0116     } else if (auto *op = dyn_cast<UnaryOperator>(stm)) {
0117         if (auto *declRef = dyn_cast<DeclRefExpr>(op->getSubExpr())) {
0118             ValueDecl *valueDecl = declRef->getDecl();
0119             auto type = op->getOpcode();
0120             if (type != UnaryOperatorKind::UO_Deref && type != UnaryOperatorKind::UO_AddrOf) {
0121                 if (valueDecl && sm().isBeforeInSLocAddrSpace(valueDecl->getBeginLoc(), stmStart)) {
0122                     // llvm::errs() << "reason5 " << op->getOpcodeStr() << "\n";
0123                     warn = true;
0124                 }
0125             }
0126         }
0127     }
0128 
0129     if (warn) {
0130         emitWarning(stmStart, "Code inside Q_ASSERT has side-effects but won't be built in release mode");
0131     }
0132 }