File indexing completed on 2024-05-12 05:40:59
0001 /* 0002 SPDX-FileCopyrightText: 2016-2018 Sergio Martins <smartins@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "function-args-by-value.h" 0008 #include "ClazyContext.h" 0009 #include "FixItUtils.h" 0010 #include "StringUtils.h" 0011 #include "TypeUtils.h" 0012 #include "Utils.h" 0013 #include "clazy_stl.h" 0014 0015 #include <clang/AST/ASTContext.h> 0016 #include <clang/AST/Decl.h> 0017 #include <clang/AST/DeclCXX.h> 0018 #include <clang/AST/Expr.h> 0019 #include <clang/AST/ExprCXX.h> 0020 #include <clang/AST/PrettyPrinter.h> 0021 #include <clang/AST/Redeclarable.h> 0022 #include <clang/AST/Stmt.h> 0023 #include <clang/AST/Type.h> 0024 #include <clang/Basic/LLVM.h> 0025 #include <clang/Basic/SourceLocation.h> 0026 #include <llvm/ADT/ArrayRef.h> 0027 #include <llvm/ADT/StringRef.h> 0028 #include <llvm/Support/Casting.h> 0029 #include <llvm/Support/raw_ostream.h> 0030 0031 #include <iterator> 0032 #include <vector> 0033 0034 namespace clang 0035 { 0036 class Decl; 0037 } // namespace clang 0038 0039 using namespace clang; 0040 0041 // TODO, go over all these 0042 bool FunctionArgsByValue::shouldIgnoreClass(CXXRecordDecl *record) 0043 { 0044 if (!record) { 0045 return false; 0046 } 0047 0048 if (Utils::isSharedPointer(record)) { 0049 return true; 0050 } 0051 0052 static const std::vector<std::string> ignoreList = { 0053 "QDebug", // Too many warnings 0054 "QGenericReturnArgument", 0055 "QColor", // TODO: Remove in Qt6 0056 "QStringRef", // TODO: Remove in Qt6 0057 "QList::const_iterator", // TODO: Remove in Qt6 0058 "QJsonArray::const_iterator", // TODO: Remove in Qt6 0059 "QList<QString>::const_iterator", // TODO: Remove in Qt6 0060 "QtMetaTypePrivate::QSequentialIterableImpl", 0061 "QtMetaTypePrivate::QAssociativeIterableImpl", 0062 "QVariantComparisonHelper", 0063 "QHashDummyValue", 0064 "QCharRef", 0065 "QString::Null", 0066 }; 0067 return clazy::contains(ignoreList, record->getQualifiedNameAsString()); 0068 } 0069 0070 bool FunctionArgsByValue::shouldIgnoreOperator(FunctionDecl *function) 0071 { 0072 // Too many warnings in operator<< 0073 static const std::vector<StringRef> ignoreList = {"operator<<"}; 0074 0075 return clazy::contains(ignoreList, clazy::name(function)); 0076 } 0077 0078 bool FunctionArgsByValue::shouldIgnoreFunction(clang::FunctionDecl *function) 0079 { 0080 static const std::vector<std::string> qualifiedIgnoreList = { 0081 "QDBusMessage::createErrorReply", // Fixed in Qt6 0082 "QMenu::exec", // Fixed in Qt6 0083 "QTextFrame::iterator", // Fixed in Qt6 0084 "QGraphicsWidget::addActions", // Fixed in Qt6 0085 "QListWidget::mimeData", // Fixed in Qt6 0086 "QTableWidget::mimeData", // Fixed in Qt6 0087 "QTreeWidget::mimeData", // Fixed in Qt6 0088 "QWidget::addActions", // Fixed in Qt6 0089 "QSslCertificate::verify", // Fixed in Qt6 0090 "QSslConfiguration::setAllowedNextProtocols", // Fixed in Qt6 0091 }; 0092 0093 return clazy::contains(qualifiedIgnoreList, function->getQualifiedNameAsString()); 0094 } 0095 0096 FunctionArgsByValue::FunctionArgsByValue(const std::string &name, ClazyContext *context) 0097 : CheckBase(name, context, Option_CanIgnoreIncludes) 0098 { 0099 } 0100 0101 void FunctionArgsByValue::VisitDecl(Decl *decl) 0102 { 0103 processFunction(dyn_cast<FunctionDecl>(decl)); 0104 } 0105 0106 void FunctionArgsByValue::VisitStmt(Stmt *stmt) 0107 { 0108 if (auto *lambda = dyn_cast<LambdaExpr>(stmt)) { 0109 processFunction(lambda->getCallOperator()); 0110 } 0111 } 0112 0113 void FunctionArgsByValue::processFunction(FunctionDecl *func) 0114 { 0115 if (!func || !func->isThisDeclarationADefinition() || func->isDeleted()) { 0116 return; 0117 } 0118 0119 auto *ctor = dyn_cast<CXXConstructorDecl>(func); 0120 if (ctor && ctor->isCopyConstructor()) { 0121 return; // copy-ctor must take by ref 0122 } 0123 0124 const bool warnForOverriddenMethods = isOptionSet("warn-for-overridden-methods"); 0125 if (!warnForOverriddenMethods && Utils::methodOverrides(dyn_cast<CXXMethodDecl>(func))) { 0126 // When overriding you can't change the signature. You should fix the base classes first 0127 return; 0128 } 0129 0130 if (shouldIgnoreOperator(func)) { 0131 return; 0132 } 0133 0134 if (m_context->isQtDeveloper() && shouldIgnoreFunction(func)) { 0135 return; 0136 } 0137 0138 Stmt *body = func->getBody(); 0139 0140 int i = -1; 0141 for (auto *param : Utils::functionParameters(func)) { 0142 i++; 0143 const QualType paramQt = clazy::unrefQualType(param->getType()); 0144 const Type *paramType = paramQt.getTypePtrOrNull(); 0145 if (!paramType || paramType->isIncompleteType() || paramType->isDependentType()) { 0146 continue; 0147 } 0148 0149 if (shouldIgnoreClass(paramType->getAsCXXRecordDecl())) { 0150 continue; 0151 } 0152 0153 clazy::QualTypeClassification classif; 0154 bool success = clazy::classifyQualType(m_context, param->getType(), param, classif, body); 0155 if (!success) { 0156 continue; 0157 } 0158 0159 if (classif.passSmallTrivialByValue) { 0160 if (ctor) { // Implements fix for Bug #379342 0161 std::vector<CXXCtorInitializer *> initializers = Utils::ctorInitializer(ctor, param); 0162 bool found_by_ref_member_init = false; 0163 for (auto *initializer : initializers) { 0164 if (!initializer->isMemberInitializer()) { 0165 continue; // skip base class initializer 0166 } 0167 FieldDecl *field = initializer->getMember(); 0168 if (!field) { 0169 continue; 0170 } 0171 0172 QualType type = field->getType(); 0173 if (type.isNull() || type->isReferenceType()) { 0174 found_by_ref_member_init = true; 0175 break; 0176 } 0177 } 0178 0179 if (found_by_ref_member_init) { 0180 continue; 0181 } 0182 } 0183 0184 std::vector<FixItHint> fixits; 0185 auto *method = dyn_cast<CXXMethodDecl>(func); 0186 const bool isVirtualMethod = method && method->isVirtual(); 0187 if (!isVirtualMethod || warnForOverriddenMethods) { // Don't try to fix virtual methods, as build can fail 0188 for (auto *redecl : func->redecls()) { // Fix in both header and .cpp 0189 auto *fdecl = dyn_cast<FunctionDecl>(redecl); 0190 const ParmVarDecl *param = fdecl->getParamDecl(i); 0191 fixits.push_back(fixit(fdecl, param, classif)); 0192 } 0193 } 0194 0195 const std::string paramStr = param->getType().getAsString(lo()); 0196 std::string error = "Pass small and trivially-copyable type by value (" + paramStr + ')'; 0197 emitWarning(param->getBeginLoc(), error, fixits); 0198 } 0199 } 0200 } 0201 0202 FixItHint FunctionArgsByValue::fixit(FunctionDecl *func, const ParmVarDecl *param, clazy::QualTypeClassification) 0203 { 0204 QualType qt = clazy::unrefQualType(param->getType()); 0205 qt.removeLocalConst(); 0206 const std::string typeName = qt.getAsString(PrintingPolicy(lo())); 0207 std::string replacement = typeName + ' ' + std::string(clazy::name(param)); 0208 SourceLocation startLoc = param->getBeginLoc(); 0209 SourceLocation endLoc = param->getEndLoc(); 0210 0211 const int numRedeclarations = std::distance(func->redecls_begin(), func->redecls_end()); 0212 const bool definitionIsAlsoDeclaration = numRedeclarations == 1; 0213 const bool isDeclarationButNotDefinition = !func->doesThisDeclarationHaveABody(); 0214 0215 if (param->hasDefaultArg() && (isDeclarationButNotDefinition || definitionIsAlsoDeclaration)) { 0216 endLoc = param->getDefaultArg()->getBeginLoc().getLocWithOffset(-1); 0217 replacement += " ="; 0218 } 0219 0220 if (!startLoc.isValid() || !endLoc.isValid()) { 0221 llvm::errs() << "Internal error could not apply fixit " << startLoc.printToString(sm()) << ';' << endLoc.printToString(sm()) << "\n"; 0222 return {}; 0223 } 0224 0225 return clazy::createReplacement({startLoc, endLoc}, replacement); 0226 }