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

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 "writing-to-temporary.h"
0011 #include "Utils.h"
0012 #include "clazy_stl.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/AST/Type.h>
0019 #include <clang/Basic/LLVM.h>
0020 #include <llvm/Support/Casting.h>
0021 
0022 #include <vector>
0023 
0024 class ClazyContext;
0025 
0026 using namespace clang;
0027 
0028 WritingToTemporary::WritingToTemporary(const std::string &name, ClazyContext *context)
0029     : CheckBase(name, context, Option_CanIgnoreIncludes)
0030     , m_widenCriteria(isOptionSet("widen-criteria"))
0031 {
0032     m_filesToIgnore = {"qstring.h"};
0033 }
0034 
0035 static bool isDisallowedClass(const std::string &className)
0036 {
0037     static const std::vector<std::string> disallowed =
0038         {"QTextCursor", "QDomElement", "KConfigGroup", "QWebElement", "QScriptValue", "QTextLine", "QTextBlock", "QDomNode", "QJSValue", "QTextTableCell"};
0039     return clazy::contains(disallowed, className);
0040 }
0041 
0042 static bool isDisallowedMethod(const std::string &name)
0043 {
0044     static const std::vector<std::string> disallowed = {"QColor::getCmyk", "QColor::getCmykF"};
0045     return clazy::contains(disallowed, name);
0046 }
0047 
0048 static bool isKnownType(const std::string &className)
0049 {
0050     static const std::vector<std::string> types = {"QList",           "QVector",     "QMap",        "QHash",  "QString", "QSet",      "QByteArray", "QUrl",
0051                                                    "QVarLengthArray", "QLinkedList", "QRect",       "QRectF", "QBitmap", "QVector2D", "QVector3D",  "QVector4D",
0052                                                    "QSize",           "QSizeF",      "QSizePolicy", "QPoint", "QPointF", "QColor"};
0053 
0054     return clazy::contains(types, className);
0055 }
0056 
0057 void WritingToTemporary::VisitStmt(clang::Stmt *stmt)
0058 {
0059     auto *callExpr = dyn_cast<CallExpr>(stmt);
0060     if (!callExpr) {
0061         return;
0062     }
0063 
0064     if (shouldIgnoreFile(stmt->getBeginLoc())) {
0065         return;
0066     }
0067 
0068     // For a chain like getFoo().setBar(), returns {setBar(), getFoo()}
0069     std::vector<CallExpr *> callExprs = Utils::callListForChain(callExpr); // callExpr is the call to setBar()
0070     if (callExprs.size() < 2) {
0071         return;
0072     }
0073 
0074     CallExpr *firstCallToBeEvaluated = callExprs.at(callExprs.size() - 1); // This is the call to getFoo()
0075     FunctionDecl *firstFunc = firstCallToBeEvaluated->getDirectCallee();
0076     if (!firstFunc) {
0077         return;
0078     }
0079 
0080     CallExpr *secondCallToBeEvaluated = callExprs.at(callExprs.size() - 2); // This is the call to setBar()
0081     FunctionDecl *secondFunc = secondCallToBeEvaluated->getDirectCallee();
0082     if (!secondFunc) {
0083         return;
0084     }
0085 
0086     auto *secondMethod = dyn_cast<CXXMethodDecl>(secondFunc);
0087     if (!secondMethod || secondMethod->isConst() || secondMethod->isStatic()) {
0088         return;
0089     }
0090 
0091     CXXRecordDecl *record = secondMethod->getParent();
0092     if (!record || isDisallowedClass(record->getNameAsString())) {
0093         return;
0094     }
0095 
0096     QualType qt = firstFunc->getReturnType();
0097     const Type *firstFuncReturnType = qt.getTypePtrOrNull();
0098     if (!firstFuncReturnType || firstFuncReturnType->isPointerType() || firstFuncReturnType->isReferenceType()) {
0099         return;
0100     }
0101 
0102     qt = secondFunc->getReturnType();
0103     const Type *secondFuncReturnType = qt.getTypePtrOrNull();
0104     if (!secondFuncReturnType || !secondFuncReturnType->isVoidType()) {
0105         return;
0106     }
0107 
0108     if (!m_widenCriteria && !isKnownType(record->getNameAsString()) && !clazy::startsWith(secondFunc->getNameAsString(), "set")) {
0109         return;
0110     }
0111 
0112     const std::string &methodName = secondMethod->getQualifiedNameAsString();
0113     if (isDisallowedMethod(methodName)) {
0114         return;
0115     }
0116 
0117     emitWarning(stmt->getBeginLoc(), "Call to temporary is a no-op: " + methodName);
0118 }