File indexing completed on 2024-05-12 05:40:58

0001 /*
0002     SPDX-FileCopyrightText: 2017 Sergio Martins <smartins@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "const-signal-or-slot.h"
0008 #include "AccessSpecifierManager.h"
0009 #include "ClazyContext.h"
0010 #include "QtUtils.h"
0011 #include "TypeUtils.h"
0012 
0013 #include <clang/AST/DeclCXX.h>
0014 #include <clang/AST/Expr.h>
0015 #include <clang/AST/Stmt.h>
0016 #include <clang/AST/Type.h>
0017 #include <clang/Basic/LLVM.h>
0018 #include <llvm/Support/Casting.h>
0019 
0020 namespace clang
0021 {
0022 class Decl;
0023 class FunctionDecl;
0024 } // namespace clang
0025 
0026 using namespace clang;
0027 
0028 ConstSignalOrSlot::ConstSignalOrSlot(const std::string &name, ClazyContext *context)
0029     : CheckBase(name, context, Option_CanIgnoreIncludes)
0030 {
0031     context->enableAccessSpecifierManager();
0032 }
0033 
0034 void ConstSignalOrSlot::VisitStmt(clang::Stmt *stmt)
0035 {
0036     auto *call = dyn_cast<CallExpr>(stmt);
0037     AccessSpecifierManager *accessSpecifierManager = m_context->accessSpecifierManager;
0038     if (!call || !accessSpecifierManager) {
0039         return;
0040     }
0041 
0042     FunctionDecl *func = call->getDirectCallee();
0043     if (!clazy::isConnect(func) || !clazy::connectHasPMFStyle(func)) {
0044         return;
0045     }
0046 
0047     CXXMethodDecl *slot = clazy::receiverMethodForConnect(call);
0048     if (!slot || !slot->isConst() || slot->getReturnType()->isVoidType()) { // const and returning void must do something, so not a getter
0049         return;
0050     }
0051 
0052     QtAccessSpecifierType specifierType = accessSpecifierManager->qtAccessSpecifierType(slot);
0053     if (specifierType == QtAccessSpecifier_Slot || specifierType == QtAccessSpecifier_Signal) {
0054         return; // For stuff explicitly marked as slots or signals we use VisitDecl
0055     }
0056 
0057     // Here the user is connecting to a const method, which isn't marked as slot or signal and returns non-void
0058     // Looks like a getter!
0059 
0060     emitWarning(stmt, slot->getQualifiedNameAsString() + " is not a slot, and is possibly a getter");
0061 }
0062 
0063 void ConstSignalOrSlot::VisitDecl(Decl *decl)
0064 {
0065     auto *method = dyn_cast<CXXMethodDecl>(decl);
0066     if (!method || !method->isConst()) {
0067         return;
0068     }
0069 
0070     AccessSpecifierManager *a = m_context->accessSpecifierManager;
0071     if (!a) {
0072         return;
0073     }
0074 
0075     if (method->isThisDeclarationADefinition() && !method->hasInlineBody()) { // Don't warn twice
0076         return;
0077     }
0078 
0079     CXXRecordDecl *record = method->getParent();
0080     if (clazy::derivesFrom(record, "QDBusAbstractInterface")) {
0081         return;
0082     }
0083 
0084     QtAccessSpecifierType specifierType = a->qtAccessSpecifierType(method);
0085 
0086     const bool isSlot = specifierType == QtAccessSpecifier_Slot;
0087     const bool isSignal = specifierType == QtAccessSpecifier_Signal;
0088 
0089     if (!isSlot && !isSignal) {
0090         return;
0091     }
0092 
0093     if (a->isScriptable(method)) {
0094         return;
0095     }
0096 
0097     if (isSlot && !method->getReturnType()->isVoidType()) {
0098         emitWarning(decl, "getter " + method->getQualifiedNameAsString() + " possibly mismarked as a slot");
0099     } else if (isSignal) {
0100         emitWarning(decl, "signal " + method->getQualifiedNameAsString() + " shouldn't be const");
0101     }
0102 }