File indexing completed on 2024-05-19 05:41:44
0001 /* 0002 SPDX-FileCopyrightText: 2017 Sergio Martins <smartins@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "thread-with-slots.h" 0008 #include "AccessSpecifierManager.h" 0009 #include "ClazyContext.h" 0010 #include "HierarchyUtils.h" 0011 #include "QtUtils.h" 0012 #include "TypeUtils.h" 0013 0014 #include <clang/AST/Decl.h> 0015 #include <clang/AST/DeclCXX.h> 0016 #include <clang/AST/Expr.h> 0017 #include <clang/AST/Stmt.h> 0018 #include <clang/Basic/LLVM.h> 0019 #include <llvm/ADT/StringRef.h> 0020 #include <llvm/Support/Casting.h> 0021 0022 #include <vector> 0023 0024 namespace clang 0025 { 0026 class Decl; 0027 } // namespace clang 0028 0029 using namespace clang; 0030 0031 static bool hasMutexes(Stmt *body) 0032 { 0033 auto declrefs = clazy::getStatements<DeclRefExpr>(body); 0034 for (auto *declref : declrefs) { 0035 ValueDecl *valueDecl = declref->getDecl(); 0036 if (CXXRecordDecl *record = clazy::typeAsRecord(valueDecl->getType())) { 0037 if (clazy::name(record) == "QMutex" || clazy::name(record) == "QBasicMutex") { 0038 return true; 0039 } 0040 } 0041 } 0042 0043 return false; 0044 } 0045 0046 ThreadWithSlots::ThreadWithSlots(const std::string &name, ClazyContext *context) 0047 : CheckBase(name, context) 0048 { 0049 context->enableAccessSpecifierManager(); 0050 } 0051 0052 void ThreadWithSlots::VisitStmt(clang::Stmt *stmt) 0053 { 0054 // Here we catch slots not marked as slots, we warn when the connect is made 0055 0056 auto *callExpr = dyn_cast<CallExpr>(stmt); 0057 if (!callExpr || !m_context->accessSpecifierManager) { 0058 return; 0059 } 0060 0061 FunctionDecl *connectFunc = callExpr->getDirectCallee(); 0062 if (!clazy::isConnect(connectFunc)) { 0063 return; 0064 } 0065 0066 CXXMethodDecl *slot = clazy::receiverMethodForConnect(callExpr); 0067 if (!slot || !clazy::derivesFrom(slot->getParent(), "QThread")) { 0068 return; 0069 } 0070 0071 if (clazy::name(slot->getParent()) == "QThread") { // The slots in QThread are thread safe, we're only worried about derived classes 0072 return; 0073 } 0074 0075 QtAccessSpecifierType specifierType = m_context->accessSpecifierManager->qtAccessSpecifierType(slot); 0076 if (specifierType == QtAccessSpecifier_Slot || specifierType == QtAccessSpecifier_Signal) { 0077 return; // For stuff explicitly marked as slots or signals we use VisitDecl 0078 } 0079 0080 emitWarning(slot, "Slot " + slot->getQualifiedNameAsString() + " might not run in the expected thread"); 0081 } 0082 0083 void ThreadWithSlots::VisitDecl(Decl *decl) 0084 { 0085 // Here we catch slots marked as such, and warn when they are declared 0086 0087 auto *method = dyn_cast<CXXMethodDecl>(decl); 0088 if (!method || !m_context->accessSpecifierManager || !method->isThisDeclarationADefinition() || !method->hasBody() 0089 || !clazy::derivesFrom(method->getParent(), "QThread")) { 0090 return; 0091 } 0092 0093 // The slots in QThread are thread safe, we're only worried about derived classes: 0094 if (clazy::name(method->getParent()) == "QThread") { 0095 return; 0096 } 0097 0098 // We're only interested in slots: 0099 if (m_context->accessSpecifierManager->qtAccessSpecifierType(method) != QtAccessSpecifier_Slot) { 0100 return; 0101 } 0102 0103 // Look for a mutex, or mutex locker, to avoid some false-positives 0104 Stmt *body = method->getBody(); 0105 if (hasMutexes(body)) { 0106 return; 0107 } 0108 0109 // If we use member mutexes, let's not warn either 0110 bool accessesNonMutexMember = false; 0111 auto memberexprs = clazy::getStatements<MemberExpr>(body); 0112 for (auto *memberexpr : memberexprs) { 0113 ValueDecl *valueDecl = memberexpr->getMemberDecl(); 0114 if (CXXRecordDecl *record = clazy::typeAsRecord(valueDecl->getType())) { 0115 if (clazy::name(record) == "QMutex" || clazy::name(record) == "QBasicMutex") { 0116 return; 0117 } 0118 } 0119 accessesNonMutexMember = true; 0120 } 0121 0122 if (!accessesNonMutexMember) { 0123 return; 0124 } 0125 0126 emitWarning(method, "Slot " + method->getQualifiedNameAsString() + " might not run in the expected thread"); 0127 }