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, 2018 Sergio Martins <smartins@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "function-args-by-ref.h" 0011 #include "ClazyContext.h" 0012 #include "FixItUtils.h" 0013 #include "StringUtils.h" 0014 #include "TypeUtils.h" 0015 #include "Utils.h" 0016 #include "clazy_stl.h" 0017 0018 #include <clang/AST/Decl.h> 0019 #include <clang/AST/DeclCXX.h> 0020 #include <clang/AST/ExprCXX.h> 0021 #include <clang/AST/Stmt.h> 0022 #include <clang/AST/Type.h> 0023 #include <clang/Basic/Diagnostic.h> 0024 #include <clang/Basic/LLVM.h> 0025 #include <llvm/ADT/ArrayRef.h> 0026 #include <llvm/ADT/StringRef.h> 0027 #include <llvm/Support/Casting.h> 0028 0029 #include <vector> 0030 0031 using namespace clang; 0032 0033 bool FunctionArgsByRef::shouldIgnoreClass(CXXRecordDecl *record) 0034 { 0035 if (!record) { 0036 return false; 0037 } 0038 0039 if (Utils::isSharedPointer(record)) { 0040 return true; 0041 } 0042 0043 static const std::vector<std::string> ignoreList = { 0044 "QDebug", // Too many warnings 0045 "QGenericReturnArgument", 0046 "QColor", // TODO: Remove in Qt6 0047 "QStringRef", // TODO: Remove in Qt6 0048 "QList::const_iterator", // TODO: Remove in Qt6 0049 "QJsonArray::const_iterator", // TODO: Remove in Qt6 0050 "QList<QString>::const_iterator", // TODO: Remove in Qt6 0051 "QtMetaTypePrivate::QSequentialIterableImpl", 0052 "QtMetaTypePrivate::QAssociativeIterableImpl", 0053 "QVariantComparisonHelper", 0054 "QHashDummyValue", 0055 "QCharRef", 0056 "QString::Null", 0057 }; 0058 return clazy::contains(ignoreList, record->getQualifiedNameAsString()); 0059 } 0060 0061 bool FunctionArgsByRef::shouldIgnoreOperator(FunctionDecl *function) 0062 { 0063 // Too many warnings in operator<< 0064 static const std::vector<StringRef> ignoreList = {"operator<<"}; 0065 0066 return clazy::contains(ignoreList, clazy::name(function)); 0067 } 0068 0069 bool FunctionArgsByRef::shouldIgnoreFunction(clang::FunctionDecl *function) 0070 { 0071 static const std::vector<std::string> qualifiedIgnoreList = { 0072 "QDBusMessage::createErrorReply", // Fixed in Qt6 0073 "QMenu::exec", // Fixed in Qt6 0074 "QTextFrame::iterator", // Fixed in Qt6 0075 "QGraphicsWidget::addActions", // Fixed in Qt6 0076 "QListWidget::mimeData", // Fixed in Qt6 0077 "QTableWidget::mimeData", // Fixed in Qt6 0078 "QTreeWidget::mimeData", // Fixed in Qt6 0079 "QWidget::addActions", // Fixed in Qt6 0080 "QSslCertificate::verify", // Fixed in Qt6 0081 "QSslConfiguration::setAllowedNextProtocols" // Fixed in Qt6 0082 }; 0083 0084 return clazy::contains(qualifiedIgnoreList, function->getQualifiedNameAsString()); 0085 } 0086 0087 FunctionArgsByRef::FunctionArgsByRef(const std::string &name, ClazyContext *context) 0088 : CheckBase(name, context, Option_CanIgnoreIncludes) 0089 { 0090 } 0091 0092 static std::string warningMsgForSmallType(int sizeOf, const std::string &typeName) 0093 { 0094 std::string sizeStr = std::to_string(sizeOf); 0095 return "Missing reference on large type (sizeof " + typeName + " is " + sizeStr + " bytes)"; 0096 } 0097 0098 void FunctionArgsByRef::processFunction(FunctionDecl *func) 0099 { 0100 if (!func || !func->isThisDeclarationADefinition() || func->isDeleted() || shouldIgnoreOperator(func)) { 0101 return; 0102 } 0103 0104 if (m_context->isQtDeveloper() && shouldIgnoreFunction(func)) { 0105 return; 0106 } 0107 0108 Stmt *body = func->getBody(); 0109 0110 auto funcParams = Utils::functionParameters(func); 0111 for (unsigned int i = 0; i < funcParams.size(); ++i) { 0112 ParmVarDecl *param = funcParams[i]; 0113 const QualType paramQt = clazy::unrefQualType(param->getType()); 0114 const Type *paramType = paramQt.getTypePtrOrNull(); 0115 if (!paramType || paramType->isIncompleteType() || paramType->isDependentType()) { 0116 continue; 0117 } 0118 0119 if (shouldIgnoreClass(paramType->getAsCXXRecordDecl())) { 0120 continue; 0121 } 0122 0123 clazy::QualTypeClassification classif; 0124 bool success = clazy::classifyQualType(m_context, param->getType(), param, classif, body); 0125 if (!success) { 0126 continue; 0127 } 0128 0129 std::vector<CXXCtorInitializer *> ctorInits = Utils::ctorInitializer(dyn_cast<CXXConstructorDecl>(func), param); 0130 if (Utils::ctorInitializerContainsMove(ctorInits)) { 0131 continue; 0132 } 0133 0134 if (classif.passBigTypeByConstRef || classif.passNonTriviallyCopyableByConstRef) { 0135 std::string error; 0136 std::vector<FixItHint> fixits; 0137 0138 const std::string paramStr = param->getType().getAsString(lo()); 0139 if (classif.passNonTriviallyCopyableByConstRef) { // Prefer this warning, because we might otherwise annoy user with specific size of Qt classes 0140 error = "Missing reference on non-trivial type (" + paramStr + ')'; 0141 } else if (classif.passBigTypeByConstRef) { 0142 error = warningMsgForSmallType(classif.size_of_T, paramStr); 0143 } 0144 0145 addFixits(fixits, func, i); 0146 emitWarning(param->getBeginLoc(), error, fixits); 0147 } 0148 } 0149 } 0150 0151 void FunctionArgsByRef::addFixits(std::vector<FixItHint> &fixits, FunctionDecl *func, unsigned int parmIndex) 0152 { 0153 for (auto *funcRedecl : func->redecls()) { 0154 auto funcParams = Utils::functionParameters(funcRedecl); 0155 if (funcParams.size() <= parmIndex) { 0156 return; 0157 } 0158 0159 ParmVarDecl *param = funcParams[parmIndex]; 0160 QualType paramQt = clazy::unrefQualType(param->getType()); 0161 0162 const bool isConst = paramQt.isConstQualified(); 0163 0164 if (!isConst) { 0165 SourceLocation start = param->getBeginLoc(); 0166 fixits.push_back(clazy::createInsertion(start, "const ")); 0167 } 0168 0169 SourceLocation end = param->getLocation(); 0170 fixits.push_back(clazy::createInsertion(end, "&")); 0171 } 0172 } 0173 0174 void FunctionArgsByRef::VisitDecl(Decl *decl) 0175 { 0176 processFunction(dyn_cast<FunctionDecl>(decl)); 0177 } 0178 0179 void FunctionArgsByRef::VisitStmt(Stmt *stmt) 0180 { 0181 if (auto *lambda = dyn_cast<LambdaExpr>(stmt)) { 0182 if (!shouldIgnoreFile(stmt->getBeginLoc())) { 0183 processFunction(lambda->getCallOperator()); 0184 } 0185 } 0186 } 0187 0188 clang::FixItHint FunctionArgsByRef::fixit(const ParmVarDecl *, clazy::QualTypeClassification) 0189 { 0190 FixItHint fixit; 0191 return fixit; 0192 }