diff --git a/src/checks/level1/range-loop.cpp b/src/checks/level1/range-loop.cpp index 19695b8..a72fefb 100644 --- a/src/checks/level1/range-loop.cpp +++ b/src/checks/level1/range-loop.cpp @@ -1,164 +1,164 @@ /* 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 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 "range-loop.h" #include "Utils.h" #include "QtUtils.h" #include "TypeUtils.h" #include "StringUtils.h" #include "LoopUtils.h" #include "StmtBodyRange.h" #include "SourceCompatibilityHelpers.h" #include "FixItUtils.h" #include "ClazyContext.h" #include "PreProcessorVisitor.h" #include #include #include #include #include #include #include #include class ClazyContext; using namespace clang; using namespace std; enum Fixit { Fixit_AddRef = 1, Fixit_AddqAsConst = 2 }; RangeLoop::RangeLoop(const std::string &name, ClazyContext *context) : CheckBase(name, context, Option_CanIgnoreIncludes) { if (fixitsEnabled()) { context->enablePreprocessorVisitor(); } } void RangeLoop::VisitStmt(clang::Stmt *stmt) { if (auto rangeLoop = dyn_cast(stmt)) { processForRangeLoop(rangeLoop); } } bool RangeLoop::islvalue(Expr *exp, SourceLocation &endLoc) { if (isa(exp)) { endLoc = clazy::locForEndOfToken(&m_astContext, clazy::getLocStart(exp)); return true; } if (auto me = dyn_cast(exp)) { auto decl = me->getMemberDecl(); if (!decl || isa(decl)) return false; endLoc = clazy::locForEndOfToken(&m_astContext, me->getMemberLoc()); return true; } return false; } void RangeLoop::processForRangeLoop(CXXForRangeStmt *rangeLoop) { Expr *containerExpr = rangeLoop->getRangeInit(); if (!containerExpr) return; QualType qt = containerExpr->getType(); const Type *t = qt.getTypePtrOrNull(); if (!t || !t->isRecordType()) return; checkPassByConstRefCorrectness(rangeLoop); if (qt.isConstQualified()) // const won't detach return; auto loopVariableType = rangeLoop->getLoopVariable()->getType(); if (!TypeUtils::unrefQualType(loopVariableType).isConstQualified() && loopVariableType->isReferenceType()) return; CXXRecordDecl *record = t->getAsCXXRecordDecl(); if (!clazy::isQtCOWIterableClass(Utils::rootBaseClass(record))) return; StmtBodyRange bodyRange(nullptr, &sm(), clazy::getLocStart(rangeLoop)); if (clazy::containerNeverDetaches(clazy::containerDeclForLoop(rangeLoop), bodyRange)) return; std::vector fixits; SourceLocation end; - if (fixitsEnabled() && islvalue(containerExpr, end)) { + if (fixitsEnabled() && islvalue(containerExpr, /*by-ref*/end)) { PreProcessorVisitor *preProcessorVisitor = m_context->preprocessorVisitor; if (!preProcessorVisitor || preProcessorVisitor->qtVersion() >= 50700) { // qAsConst() was added to 5.7 SourceLocation start = clazy::getLocStart(containerExpr); fixits.push_back(clazy::createInsertion(start, "qAsConst(")); //SourceLocation end = getLocEnd(containerExpr); fixits.push_back(clazy::createInsertion(end, ")")); } } emitWarning(clazy::getLocStart(rangeLoop), "c++11 range-loop might detach Qt container (" + record->getQualifiedNameAsString() + ')', fixits); } void RangeLoop::checkPassByConstRefCorrectness(CXXForRangeStmt *rangeLoop) { TypeUtils::QualTypeClassification classif; auto varDecl = rangeLoop->getLoopVariable(); bool success = TypeUtils::classifyQualType(m_context, varDecl, /*by-ref*/ classif, rangeLoop); if (!success) return; if (classif.passNonTriviallyCopyableByConstRef) { string msg; const string paramStr = clazy::simpleTypeName(varDecl->getType(), lo()); msg = "Missing reference in range-for with non trivial type (" + paramStr + ')'; std::vector fixits; if (fixitsEnabled()) { const bool isConst = varDecl->getType().isConstQualified(); if (!isConst) { SourceLocation start = clazy::getLocStart(varDecl); fixits.push_back(clazy::createInsertion(start, "const ")); } SourceLocation end = varDecl->getLocation(); fixits.push_back(clazy::createInsertion(end, "&")); } // We ignore classif.passSmallTrivialByValue because it doesn't matter, the compiler is able // to optimize it, generating the same assembly, regardless of pass by value. emitWarning(clazy::getLocStart(varDecl), msg.c_str(), fixits); } }