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 }