diff --git a/src/checks/level0/temporaryiterator.cpp b/src/checks/level0/temporaryiterator.cpp index 56bb5cb..d3825bb 100644 --- a/src/checks/level0/temporaryiterator.cpp +++ b/src/checks/level0/temporaryiterator.cpp @@ -1,151 +1,150 @@ /* This file is part of the clazy static checker. Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Sérgio Martins Copyright (C) 2015 Nyall Dawson Copyright (C) 2015-2016 Sergio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "checkmanager.h" #include "temporaryiterator.h" #include "Utils.h" #include "HierarchyUtils.h" #include "StringUtils.h" #include #include #include #include using namespace clang; using namespace std; TemporaryIterator::TemporaryIterator(const std::string &name, const clang::CompilerInstance &ci) : CheckBase(name, ci) { m_methodsByType["vector"] = {"begin", "end", "cbegin", "cend" }; // TODO: More stl support m_methodsByType["QList"] = { "begin", "end", "constBegin", "constEnd", "cbegin", "cend" }; m_methodsByType["QVector"] = { "begin", "end","constBegin","constEnd", "cbegin", "cend", "insert" }; m_methodsByType["QMap"] = {"begin", "end", "constBegin", "constEnd", "find", "constFind", "lowerBound","upperBound", "cbegin", "cend", "equal_range" }; m_methodsByType["QHash"] = {"begin", "end", "constBegin", "constEnd", "cbegin", "cend", "find", "constFind", "insert", "insertMulti" }; m_methodsByType["QLinkedList"] = {"begin", "end", "constBegin", "constEnd", "cbegin", "cend"}; m_methodsByType["QSet"] = {"begin", "end", "constBegin", "constEnd", "find", "constFind", "cbegin", "cend"}; m_methodsByType["QStack"] = m_methodsByType["QVector"]; m_methodsByType["QQueue"] = m_methodsByType["QList"]; m_methodsByType["QMultiMap"] = m_methodsByType["QMap"]; m_methodsByType["QMultiHash"] = m_methodsByType["QHash"]; } static bool isBlacklistedFunction(const string &name) { // These are fine static const vector list = {"QVariant::toList", "QHash::operator[]", "QMap::operator[]", "QSet::operator[]"}; return clazy_std::contains(list, name); } void TemporaryIterator::VisitStmt(clang::Stmt *stm) { CXXMemberCallExpr *memberExpr = dyn_cast(stm); if (!memberExpr) return; CXXRecordDecl *classDecl = memberExpr->getRecordDecl(); CXXMethodDecl *methodDecl = memberExpr->getMethodDecl(); if (!classDecl || !methodDecl) return; // Check if it's a container const std::string className = classDecl->getNameAsString(); auto it = m_methodsByType.find(className); if (it == m_methodsByType.end()) return; // Check if it's a method returning an iterator const std::string functionName = methodDecl->getNameAsString(); const auto &allowedFunctions = it->second; if (!clazy_std::contains(allowedFunctions, functionName)) return; // Catch getList().cbegin().value(), which is ok if (HierarchyUtils::getFirstParentOfType(m_parentMap, m_parentMap->getParent(memberExpr))) return; // Catch variant.toList().cbegin(), which is ok CXXMemberCallExpr *chainedMemberCall = HierarchyUtils::getFirstChildOfType(memberExpr); if (chainedMemberCall) { if (isBlacklistedFunction(StringUtils::qualifiedMethodName(chainedMemberCall->getMethodDecl()))) return; } // catch map[foo].cbegin() CXXOperatorCallExpr *chainedOperatorCall = HierarchyUtils::getFirstChildOfType(memberExpr); if (chainedOperatorCall) { FunctionDecl *func = chainedOperatorCall->getDirectCallee(); if (func) { CXXMethodDecl *method = dyn_cast(func); if (method) { if (isBlacklistedFunction(StringUtils::qualifiedMethodName(method))) return; } } } // If we deref it within the expression, then we'll copy the value before the iterator becomes invalid, so it's safe if (Utils::isInDerefExpression(memberExpr, m_parentMap)) return; Expr *expr = memberExpr->getImplicitObjectArgument(); - - if (!expr || !expr->isRValue()) // This check is about detaching temporaries, so check for r value + if (!expr || expr->isLValue()) // This check is about detaching temporaries, so check for r value return; const Type *containerType = expr->getType().getTypePtrOrNull(); if (!containerType || containerType->isPointerType()) return; { // *really* check for rvalue ImplicitCastExpr *impl = dyn_cast(expr); if (impl) { if (impl->getCastKind() == CK_LValueToRValue) return; Stmt *firstChild = HierarchyUtils::getFirstChild(impl); if (firstChild && isa(firstChild) && dyn_cast(firstChild)->getCastKind() == CK_LValueToRValue) return; } } CXXConstructExpr *possibleCtorCall = dyn_cast_or_null(HierarchyUtils::getFirstChildAtDepth(expr, 2)); if (possibleCtorCall) return; CXXThisExpr *possibleThisCall = dyn_cast_or_null(HierarchyUtils::getFirstChildAtDepth(expr, 1)); if (possibleThisCall) return; // llvm::errs() << "Expression: " << expr->getStmtClassName() << "\n"; std::string error = std::string("Don't call ") + StringUtils::qualifiedMethodName(methodDecl) + std::string("() on temporary"); emitWarning(stm->getLocStart(), error.c_str()); } REGISTER_CHECK_WITH_FLAGS("temporary-iterator", TemporaryIterator, CheckLevel0)