1 /*
2 This file is part of the clazy static checker.
3
4 Copyright (C) 2016-2018 Sergio Martins <smartins@kde.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20 */
21
22 #include "function-args-by-value.h"
23 #include "Utils.h"
24 #include "StringUtils.h"
25 #include "TypeUtils.h"
26 #include "FixItUtils.h"
27 #include "ClazyContext.h"
28 #include "SourceCompatibilityHelpers.h"
29 #include "clazy_stl.h"
30
31 #include <clang/AST/ASTContext.h>
32 #include <clang/AST/Decl.h>
33 #include <clang/AST/DeclCXX.h>
34 #include <clang/AST/Expr.h>
35 #include <clang/AST/ExprCXX.h>
36 #include <clang/AST/PrettyPrinter.h>
37 #include <clang/AST/Redeclarable.h>
38 #include <clang/AST/Stmt.h>
39 #include <clang/AST/Type.h>
40 #include <clang/Basic/LLVM.h>
41 #include <clang/Basic/SourceLocation.h>
42 #include <llvm/ADT/ArrayRef.h>
43 #include <llvm/ADT/StringRef.h>
44 #include <llvm/Support/Casting.h>
45 #include <llvm/Support/raw_ostream.h>
46
47 #include <iterator>
48 #include <vector>
49
50 namespace clang {
51 class Decl;
52 } // namespace clang
53
54 using namespace clang;
55 using namespace std;
56
57 // TODO, go over all these
shouldIgnoreClass(CXXRecordDecl * record)58 static bool shouldIgnoreClass(CXXRecordDecl *record)
59 {
60 if (!record)
61 return false;
62
63 if (Utils::isSharedPointer(record))
64 return true;
65
66 static const vector<string> ignoreList = {"QDebug", // Too many warnings
67 "QGenericReturnArgument",
68 "QColor", // TODO: Remove in Qt6
69 "QStringRef", // TODO: Remove in Qt6
70 "QList::const_iterator", // TODO: Remove in Qt6
71 "QJsonArray::const_iterator", // TODO: Remove in Qt6
72 "QList<QString>::const_iterator", // TODO: Remove in Qt6
73 "QtMetaTypePrivate::QSequentialIterableImpl",
74 "QtMetaTypePrivate::QAssociativeIterableImpl",
75 "QVariantComparisonHelper",
76 "QHashDummyValue", "QCharRef", "QString::Null"
77 };
78 return clazy::contains(ignoreList, record->getQualifiedNameAsString());
79 }
80
shouldIgnoreOperator(FunctionDecl * function)81 static bool shouldIgnoreOperator(FunctionDecl *function)
82 {
83 // Too many warnings in operator<<
84 static const vector<StringRef> ignoreList = { "operator<<" };
85
86 return clazy::contains(ignoreList, clazy::name(function));
87 }
88
shouldIgnoreFunction(clang::FunctionDecl * function)89 static bool shouldIgnoreFunction(clang::FunctionDecl *function)
90 {
91 static const vector<string> qualifiedIgnoreList = {"QDBusMessage::createErrorReply", // Fixed in Qt6
92 "QMenu::exec", // Fixed in Qt6
93 "QTextFrame::iterator", // Fixed in Qt6
94 "QGraphicsWidget::addActions", // Fixed in Qt6
95 "QListWidget::mimeData", // Fixed in Qt6
96 "QTableWidget::mimeData", // Fixed in Qt6
97 "QTreeWidget::mimeData", // Fixed in Qt6
98 "QWidget::addActions", // Fixed in Qt6
99 "QSslCertificate::verify", // Fixed in Qt6
100 "QSslConfiguration::setAllowedNextProtocols" // Fixed in Qt6
101 };
102
103 return clazy::contains(qualifiedIgnoreList, function->getQualifiedNameAsString());
104 }
105
FunctionArgsByValue(const std::string & name,ClazyContext * context)106 FunctionArgsByValue::FunctionArgsByValue(const std::string &name, ClazyContext *context)
107 : CheckBase(name, context, Option_CanIgnoreIncludes)
108 {
109 }
110
VisitDecl(Decl * decl)111 void FunctionArgsByValue::VisitDecl(Decl *decl)
112 {
113 processFunction(dyn_cast<FunctionDecl>(decl));
114 }
115
VisitStmt(Stmt * stmt)116 void FunctionArgsByValue::VisitStmt(Stmt *stmt)
117 {
118 if (auto lambda = dyn_cast<LambdaExpr>(stmt))
119 processFunction(lambda->getCallOperator());
120 }
121
processFunction(FunctionDecl * func)122 void FunctionArgsByValue::processFunction(FunctionDecl *func)
123 {
124 if (!func || !func->isThisDeclarationADefinition() || func->isDeleted())
125 return;
126
127 auto ctor = dyn_cast<CXXConstructorDecl>(func);
128 if (ctor && ctor->isCopyConstructor())
129 return; // copy-ctor must take by ref
130
131 const bool warnForOverriddenMethods = isOptionSet("warn-for-overridden-methods");
132 if (!warnForOverriddenMethods && Utils::methodOverrides(dyn_cast<CXXMethodDecl>(func))) {
133 // When overriding you can't change the signature. You should fix the base classes first
134 return;
135 }
136
137 if (shouldIgnoreOperator(func))
138 return;
139
140 if (m_context->isQtDeveloper() && shouldIgnoreFunction(func))
141 return;
142
143 Stmt *body = func->getBody();
144
145 int i = -1;
146 for (auto param : Utils::functionParameters(func)) {
147 i++;
148 QualType paramQt = TypeUtils::unrefQualType(param->getType());
149 const Type *paramType = paramQt.getTypePtrOrNull();
150 if (!paramType || paramType->isIncompleteType() || paramType->isDependentType())
151 continue;
152
153 if (shouldIgnoreClass(paramType->getAsCXXRecordDecl()))
154 continue;
155
156 TypeUtils::QualTypeClassification classif;
157 bool success = TypeUtils::classifyQualType(m_context, param, classif, body);
158 if (!success)
159 continue;
160
161 if (classif.passSmallTrivialByValue) {
162 if (ctor) { // Implements fix for Bug #379342
163 vector<CXXCtorInitializer *> initializers = Utils::ctorInitializer(ctor, param);
164 bool found_by_ref_member_init = false;
165 for (auto initializer : initializers) {
166 if (!initializer->isMemberInitializer())
167 continue; // skip base class initializer
168 FieldDecl *field = initializer->getMember();
169 if (!field)
170 continue;
171
172 QualType type = field->getType();
173 if (type.isNull() || type->isReferenceType()) {
174 found_by_ref_member_init = true;
175 break;
176 }
177 }
178
179 if (found_by_ref_member_init)
180 continue;
181 }
182
183 std::vector<FixItHint> fixits;
184 auto method = dyn_cast<CXXMethodDecl>(func);
185 const bool isVirtualMethod = method && method->isVirtual();
186 if ((!isVirtualMethod || warnForOverriddenMethods) && isFixitEnabled()) { // Don't try to fix virtual methods, as build can fail
187 for (auto redecl : func->redecls()) { // Fix in both header and .cpp
188 auto fdecl = dyn_cast<FunctionDecl>(redecl);
189 const ParmVarDecl *param = fdecl->getParamDecl(i);
190 fixits.push_back(fixit(fdecl, param, classif));
191 }
192 }
193
194 const string paramStr = param->getType().getAsString();
195 string error = "Pass small and trivially-copyable type by value (" + paramStr + ')';
196 emitWarning(clazy::getLocStart(param), error.c_str(), fixits);
197 }
198 }
199 }
200
fixit(FunctionDecl * func,const ParmVarDecl * param,TypeUtils::QualTypeClassification)201 FixItHint FunctionArgsByValue::fixit(FunctionDecl *func, const ParmVarDecl *param,
202 TypeUtils::QualTypeClassification)
203 {
204 QualType qt = TypeUtils::unrefQualType(param->getType());
205 qt.removeLocalConst();
206 const string typeName = qt.getAsString(PrintingPolicy(lo()));
207 string replacement = typeName + ' ' + string(clazy::name(param));
208 SourceLocation startLoc = clazy::getLocStart(param);
209 SourceLocation endLoc = clazy::getLocEnd(param);
210
211 const int numRedeclarations = std::distance(func->redecls_begin(), func->redecls_end());
212 const bool definitionIsAlsoDeclaration = numRedeclarations == 1;
213 const bool isDeclarationButNotDefinition = !func->doesThisDeclarationHaveABody();
214
215 if (param->hasDefaultArg() && (isDeclarationButNotDefinition || definitionIsAlsoDeclaration)) {
216 endLoc = clazy::getLocStart(param->getDefaultArg()).getLocWithOffset(-1);
217 replacement += " =";
218 }
219
220 if (!startLoc.isValid() || !endLoc.isValid()) {
221 llvm::errs() << "Internal error could not apply fixit " << startLoc.printToString(sm())
222 << ';' << endLoc.printToString(sm()) << "\n";
223 return {};
224 }
225
226 return clazy::createReplacement({ startLoc, endLoc }, replacement);
227 }
228