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

0001 /*
0002     SPDX-FileCopyrightText: 2018 Sergio Martins <smartins@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "qrequiredresult-candidates.h"
0008 #include "QtUtils.h"
0009 #include "TypeUtils.h"
0010 #include "Utils.h"
0011 
0012 #include <clang/AST/AST.h>
0013 
0014 using namespace clang;
0015 
0016 static bool hasUnusedResultAttr(clang::FunctionDecl *func)
0017 {
0018     auto RetType = func->getReturnType();
0019     if (const auto *Ret = RetType->getAsRecordDecl()) {
0020         if (const auto *R = Ret->getAttr<clang::WarnUnusedResultAttr>()) {
0021             return R != nullptr;
0022         }
0023     } else if (const auto *ET = RetType->getAs<clang::EnumType>()) {
0024         if (const clang::EnumDecl *ED = ET->getDecl()) {
0025             if (const auto *R = ED->getAttr<clang::WarnUnusedResultAttr>()) {
0026                 return R != nullptr;
0027             }
0028         }
0029     }
0030     return func->getAttr<clang::WarnUnusedResultAttr>() != nullptr;
0031 }
0032 
0033 QRequiredResultCandidates::QRequiredResultCandidates(const std::string &name, ClazyContext *context)
0034     : CheckBase(name, context)
0035 {
0036 }
0037 
0038 void QRequiredResultCandidates::VisitDecl(clang::Decl *decl)
0039 {
0040     auto *method = dyn_cast<CXXMethodDecl>(decl);
0041     if (!method || !method->isConst()) {
0042         return;
0043     }
0044 
0045     if (method->isThisDeclarationADefinition() && !method->hasInlineBody()) { // Don't warn twice
0046         return;
0047     }
0048 
0049     if (hasUnusedResultAttr(method)) { // Also catches nodiscard
0050         return;
0051     }
0052 
0053     if (method->getAccess() == AS_private) { // We're only interested on our public API
0054         return;
0055     }
0056 
0057     QualType qt = method->getReturnType();
0058     CXXRecordDecl *returnClass = qt->getAsCXXRecordDecl();
0059     returnClass = returnClass ? returnClass->getCanonicalDecl() : nullptr;
0060     if (!returnClass) {
0061         return;
0062     }
0063 
0064     CXXRecordDecl *classDecl = method->getParent();
0065     classDecl = classDecl ? classDecl->getCanonicalDecl() : nullptr;
0066 
0067     if (classDecl->getAccess() == AS_private) { // A nested private class. We're only interested on our public API
0068         return;
0069     }
0070 
0071     if (returnClass == classDecl) {
0072         const std::string methodName = static_cast<std::string>(clazy::name(method));
0073         if (methodName.empty()) { // fixes assert
0074             return;
0075         }
0076 
0077         if (clazy::startsWith(methodName, "to") || clazy::startsWith(methodName, "operator") || !clazy::endsWith(methodName, "ed")) {
0078             return;
0079         }
0080 
0081         emitWarning(decl, "Add Q_REQUIRED_RESULT to " + method->getQualifiedNameAsString() + "()");
0082     }
0083 }