File indexing completed on 2024-05-12 05:40:59
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 "range-loop-detach.h" 0011 #include "ClazyContext.h" 0012 #include "FixItUtils.h" 0013 #include "LoopUtils.h" 0014 #include "PreProcessorVisitor.h" 0015 #include "QtUtils.h" 0016 #include "StmtBodyRange.h" 0017 #include "TypeUtils.h" 0018 #include "Utils.h" 0019 0020 #include <clang/AST/Decl.h> 0021 #include <clang/AST/DeclCXX.h> 0022 #include <clang/AST/Expr.h> 0023 #include <clang/AST/Stmt.h> 0024 #include <clang/AST/StmtCXX.h> 0025 #include <clang/AST/Type.h> 0026 #include <llvm/Support/Casting.h> 0027 0028 class ClazyContext; 0029 0030 using namespace clang; 0031 0032 namespace clazy 0033 { 0034 /** 0035 * Returns true if we can prove the container doesn't detach. 0036 * Returns false otherwise, meaning that you can't conclude anything if false is returned. 0037 * 0038 * For true to be returned, all these conditions must verify: 0039 * - Container is a local variable 0040 * - It's not passed to any function 0041 * - It's not assigned to another variable 0042 */ 0043 bool containerNeverDetaches(const clang::VarDecl *valDecl, StmtBodyRange bodyRange) // clazy:exclude=function-args-by-value 0044 { 0045 // This helps for bug 367485 0046 0047 if (!valDecl) { 0048 return false; 0049 } 0050 0051 const auto *const context = dyn_cast<FunctionDecl>(valDecl->getDeclContext()); 0052 if (!context) { 0053 return false; 0054 } 0055 0056 bodyRange.body = context->getBody(); 0057 if (!bodyRange.body) { 0058 return false; 0059 } 0060 0061 if (valDecl->hasInit()) { 0062 if (const auto *cleanupExpr = dyn_cast<clang::ExprWithCleanups>(valDecl->getInit())) { 0063 if (const auto *ce = dyn_cast<clang::CXXConstructExpr>(cleanupExpr->getSubExpr())) { 0064 if (!ce->isListInitialization() && !ce->isStdInitListInitialization()) { 0065 // When initing via copy or move ctor there's possible detachments. 0066 return false; 0067 } 0068 } else if (dyn_cast<clang::CXXBindTemporaryExpr>(cleanupExpr->getSubExpr())) { 0069 return false; 0070 } 0071 } 0072 } 0073 0074 // TODO1: Being passed to a function as const should be OK 0075 if (Utils::isPassedToFunction(bodyRange, valDecl, false)) { 0076 return false; 0077 } 0078 0079 return true; 0080 } 0081 } 0082 0083 RangeLoopDetach::RangeLoopDetach(const std::string &name, ClazyContext *context) 0084 : CheckBase(name, context, Option_CanIgnoreIncludes) 0085 { 0086 context->enablePreprocessorVisitor(); 0087 } 0088 0089 void RangeLoopDetach::VisitStmt(clang::Stmt *stmt) 0090 { 0091 if (auto *rangeLoop = dyn_cast<CXXForRangeStmt>(stmt)) { 0092 processForRangeLoop(rangeLoop); 0093 } 0094 } 0095 0096 bool RangeLoopDetach::islvalue(Expr *exp, SourceLocation &endLoc) 0097 { 0098 if (isa<DeclRefExpr>(exp)) { 0099 endLoc = clazy::locForEndOfToken(&m_astContext, exp->getBeginLoc()); 0100 return true; 0101 } 0102 0103 if (auto *me = dyn_cast<MemberExpr>(exp)) { 0104 auto *decl = me->getMemberDecl(); 0105 if (!decl || isa<FunctionDecl>(decl)) { 0106 return false; 0107 } 0108 0109 endLoc = clazy::locForEndOfToken(&m_astContext, me->getMemberLoc()); 0110 return true; 0111 } 0112 0113 return false; 0114 } 0115 0116 void RangeLoopDetach::processForRangeLoop(CXXForRangeStmt *rangeLoop) 0117 { 0118 Expr *containerExpr = rangeLoop->getRangeInit(); 0119 if (!containerExpr) { 0120 return; 0121 } 0122 0123 QualType qt = containerExpr->getType(); 0124 const Type *t = qt.getTypePtrOrNull(); 0125 if (!t || !t->isRecordType()) { 0126 return; 0127 } 0128 0129 if (qt.isConstQualified()) { // const won't detach 0130 return; 0131 } 0132 0133 auto loopVariableType = rangeLoop->getLoopVariable()->getType(); 0134 if (!clazy::unrefQualType(loopVariableType).isConstQualified() && loopVariableType->isReferenceType()) { 0135 return; 0136 } 0137 0138 CXXRecordDecl *record = t->getAsCXXRecordDecl(); 0139 if (!clazy::isQtCOWIterableClass(Utils::rootBaseClass(record))) { 0140 return; 0141 } 0142 0143 StmtBodyRange bodyRange(nullptr, &sm(), rangeLoop->getBeginLoc()); 0144 if (clazy::containerNeverDetaches(clazy::containerDeclForLoop(rangeLoop), bodyRange)) { 0145 return; 0146 } 0147 0148 std::vector<FixItHint> fixits; 0149 0150 SourceLocation end; 0151 if (islvalue(containerExpr, /*by-ref*/ end)) { 0152 PreProcessorVisitor *preProcessorVisitor = m_context->preprocessorVisitor; 0153 if (!preProcessorVisitor || preProcessorVisitor->qtVersion() >= 50700) { // qAsConst() was added to 5.7 0154 clang::SourceRange exprRange = containerExpr->getSourceRange(); 0155 llvm::StringRef exprText = Lexer::getSourceText(CharSourceRange::getTokenRange(exprRange.getBegin(), exprRange.getEnd()), sm(), lo()); 0156 std::string insertion = (lo().CPlusPlus17 ? "std::as_const(" : "qAsConst(") + exprText.str() + ")"; 0157 fixits.push_back(clazy::createReplacement(exprRange, insertion)); 0158 } 0159 } 0160 0161 auto *typedefType = t->getAs<TypedefType>(); // Typedefs in internal Qt code, like QStringList should not be resolved 0162 const std::string name = typedefType ? typedefType->getDecl()->getNameAsString() : record->getNameAsString(); 0163 emitWarning(rangeLoop->getBeginLoc(), "c++11 range-loop might detach Qt container (" + name + ')', fixits); 0164 }