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

0001 /*
0002     SPDX-FileCopyrightText: 2015 Albert Astals Cid <albert.astals@canonical.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "qdeleteall.h"
0008 #include "ClazyContext.h"
0009 #include "HierarchyUtils.h"
0010 #include "QtUtils.h"
0011 #include "StringUtils.h"
0012 
0013 #include <clang/AST/Decl.h>
0014 #include <clang/AST/DeclCXX.h>
0015 #include <clang/AST/Expr.h>
0016 #include <clang/AST/ExprCXX.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 using namespace clang;
0023 
0024 QDeleteAll::QDeleteAll(const std::string &name, ClazyContext *context)
0025     : CheckBase(name, context, Option_CanIgnoreIncludes)
0026 {
0027 }
0028 
0029 void QDeleteAll::VisitStmt(clang::Stmt *stmt)
0030 {
0031     // Find a call to QMap/QSet/QHash::values/keys
0032     auto *offendingCall = dyn_cast<CXXMemberCallExpr>(stmt);
0033     FunctionDecl *func = offendingCall ? offendingCall->getDirectCallee() : nullptr;
0034     if (!func) {
0035         return;
0036     }
0037 
0038     const std::string funcName = func->getNameAsString();
0039     const bool isValues = funcName == "values";
0040     if (!isValues && funcName != "keys") {
0041         return;
0042     }
0043     std::string offendingClassName;
0044     // In case QMultiHash::values is used in Qt5, values is defined in the QHash baseclass, look up the original record to be sure we have a QMultiHash
0045     if (auto *cast = dyn_cast<ImplicitCastExpr>(offendingCall->getImplicitObjectArgument())) {
0046         if (auto *subExpr = dyn_cast<DeclRefExpr>(cast->getSubExpr())) {
0047             if (auto *ptr = subExpr->getType().getTypePtrOrNull(); ptr && ptr->isRecordType()) {
0048                 offendingClassName = ptr->getAsRecordDecl()->getNameAsString();
0049             }
0050         }
0051     }
0052     // Check if we have whitelisted the classname
0053     if (offendingClassName.empty() || !clazy::isQtAssociativeContainer(offendingClassName)) {
0054         return;
0055     }
0056 
0057     // Once found see if the first parent call is qDeleteAll
0058     int i = 1;
0059     Stmt *p = clazy::parent(m_context->parentMap, stmt, i);
0060     while (p) {
0061         auto *pc = dyn_cast<CallExpr>(p);
0062         if (FunctionDecl *f = pc ? pc->getDirectCallee() : nullptr) {
0063             if (clazy::name(f) == "qDeleteAll") {
0064                 std::string msg = "qDeleteAll() is being used on an unnecessary temporary container created by " + offendingClassName + "::" + funcName + "()";
0065                 if (func->getNumParams() == 0) { // Ignore values method calls where lookup-parameter is given, like the deprecated QHash::values(Key)
0066                     if (isValues) {
0067                         msg += ", use qDeleteAll(mycontainer) instead";
0068                     } else {
0069                         msg += ", use qDeleteAll(mycontainer.keyBegin(), mycontainer.keyEnd()) instead";
0070                     }
0071                     emitWarning(p->getBeginLoc(), msg);
0072                 }
0073             }
0074             break;
0075         }
0076         ++i;
0077         p = clazy::parent(m_context->parentMap, stmt, i);
0078     }
0079 }