File indexing completed on 2024-05-12 05:40:55
0001 /* 0002 SPDX-FileCopyrightText: 2016 Sergio Martins <smartins@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "container-anti-pattern.h" 0008 #include "HierarchyUtils.h" 0009 #include "LoopUtils.h" 0010 #include "StringUtils.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/ExprCXX.h> 0018 #include <clang/AST/Stmt.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 ContainerAntiPattern::ContainerAntiPattern(const std::string &name, ClazyContext *context) 0029 : CheckBase(name, context, Option_CanIgnoreIncludes) 0030 { 0031 } 0032 0033 static bool isInterestingCall(CallExpr *call) 0034 { 0035 FunctionDecl *func = call ? call->getDirectCallee() : nullptr; 0036 if (!func) { 0037 return false; 0038 } 0039 0040 static const std::vector<std::string> methods = { 0041 "QVector::toList", 0042 "QList::toVector", 0043 "QMap::values", 0044 "QMap::keys", 0045 "QSet::toList", 0046 "QSet::values", 0047 "QHash::values", 0048 "QHash::keys", 0049 "QList::toList", // In case one has code compiling against Qt6, but Qt5 compat 0050 }; 0051 0052 return clazy::contains(methods, clazy::qualifiedMethodName(func)); 0053 } 0054 0055 void ContainerAntiPattern::VisitStmt(clang::Stmt *stmt) 0056 { 0057 if (handleLoop(stmt)) { // catch for (auto i : map.values()) and such 0058 return; 0059 } 0060 0061 if (VisitQSet(stmt)) { 0062 return; 0063 } 0064 0065 std::vector<CallExpr *> calls = Utils::callListForChain(dyn_cast<CallExpr>(stmt)); 0066 if (calls.size() < 2) { 0067 return; 0068 } 0069 0070 // For an expression like set.toList().count()... 0071 CallExpr *callexpr1 = calls[calls.size() - 1]; // ...this would be toList() 0072 // CallExpr *callexpr2 = calls[calls.size() - 2]; // ...and this would be count() 0073 0074 if (!isInterestingCall(callexpr1)) { 0075 return; 0076 } 0077 0078 emitWarning(stmt->getBeginLoc(), "allocating an unneeded temporary container"); 0079 } 0080 0081 bool ContainerAntiPattern::VisitQSet(Stmt *stmt) 0082 { 0083 auto *secondCall = dyn_cast<CXXMemberCallExpr>(stmt); 0084 if (!secondCall || !secondCall->getMethodDecl()) { 0085 return false; 0086 } 0087 0088 CXXMethodDecl *secondMethod = secondCall->getMethodDecl(); 0089 const std::string secondMethodName = clazy::qualifiedMethodName(secondMethod); 0090 if (secondMethodName != "QSet::isEmpty") { 0091 return false; 0092 } 0093 0094 std::vector<CallExpr *> chainedCalls = Utils::callListForChain(secondCall); 0095 if (chainedCalls.size() < 2) { 0096 return false; 0097 } 0098 0099 CallExpr *firstCall = chainedCalls[chainedCalls.size() - 1]; 0100 FunctionDecl *firstFunc = firstCall->getDirectCallee(); 0101 if (!firstFunc) { 0102 return false; 0103 } 0104 0105 auto *firstMethod = dyn_cast<CXXMethodDecl>(firstFunc); 0106 if (!firstMethod || clazy::qualifiedMethodName(firstMethod) != "QSet::intersect") { 0107 return false; 0108 } 0109 0110 emitWarning(stmt->getBeginLoc(), "Use QSet::intersects() instead"); 0111 return true; 0112 } 0113 0114 bool ContainerAntiPattern::handleLoop(Stmt *stm) 0115 { 0116 Expr *containerExpr = clazy::containerExprForLoop(stm); 0117 if (!containerExpr) { 0118 return false; 0119 } 0120 0121 auto *memberExpr = clazy::getFirstChildOfType2<CXXMemberCallExpr>(containerExpr); 0122 if (isInterestingCall(memberExpr)) { 0123 emitWarning(stm->getBeginLoc(), "allocating an unneeded temporary container"); 0124 return true; 0125 } 0126 0127 return false; 0128 }