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 }