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 }