lok: getSlideShowInfo: interactions: check that properties are available
[LibreOffice.git] / compilerplugins / clang / passparamsbyref.cxx
blob1cdf5272390b9d7e29ee8af49d475c4096484201
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9 #ifndef LO_CLANG_SHARED_PLUGINS
11 #include <string>
12 #include <unordered_set>
14 #include "check.hxx"
15 #include "compat.hxx"
16 #include "plugin.hxx"
18 // Find places where parameters are passed by value
19 // It's not very efficient, because we generally end up copying it twice - once into the parameter and
20 // again into the destination.
21 // They should rather be passed by reference.
23 // Generally recommending lambda capture by-ref rather than by-copy is even more
24 // problematic than with function parameters, as a lambda instance can easily
25 // outlive a referenced variable. So once lambdas start to get used in more
26 // sophisticated ways than passing them into standard algorithms, this plugin's
27 // advice, at least for explicit captures, will need to be revisited.
29 namespace {
31 class PassParamsByRef:
32 public loplugin::FilteringPlugin<PassParamsByRef>
34 public:
35 explicit PassParamsByRef(loplugin::InstantiationData const & data): FilteringPlugin(data), mbInsideFunctionDecl(false) {}
37 virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
39 // When warning about function params of primitive type that could be passed
40 // by value instead of by reference, make sure not to warn if the parameter
41 // is ever bound to a reference; on the one hand, this needs scaffolding in
42 // all Traverse*Decl functions (indirectly) derived from FunctionDecl; and
43 // on the other hand, use a hack of ignoring just the DeclRefExprs nested in
44 // LValueToRValue ImplicitCastExprs when determining whether a param is
45 // bound to a reference:
46 bool PreTraverseFunctionDecl(FunctionDecl *);
47 bool PostTraverseFunctionDecl(FunctionDecl *, bool);
48 bool TraverseFunctionDecl(FunctionDecl *);
49 bool PreTraverseCXXMethodDecl(CXXMethodDecl *);
50 bool PostTraverseCXXMethodDecl(CXXMethodDecl *, bool);
51 bool TraverseCXXMethodDecl(CXXMethodDecl *);
52 bool PreTraverseCXXConstructorDecl(CXXConstructorDecl *);
53 bool PostTraverseCXXConstructorDecl(CXXConstructorDecl *, bool);
54 bool TraverseCXXConstructorDecl(CXXConstructorDecl *);
55 bool PreTraverseImplicitCastExpr(ImplicitCastExpr *);
56 bool TraverseImplicitCastExpr(ImplicitCastExpr *);
58 bool VisitBinaryOperator(BinaryOperator const *);
59 bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *);
60 bool VisitCallExpr(const CallExpr *);
61 bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *);
63 private:
64 bool isFat(QualType type);
66 bool mbInsideFunctionDecl;
67 std::unordered_set<ParmVarDecl const *> mParamExclusions;
70 bool PassParamsByRef::PreTraverseFunctionDecl(FunctionDecl* functionDecl)
72 if (ignoreLocation(functionDecl))
73 return false;
74 if (functionDecl->isDeleted()
75 || functionDecl->isFunctionTemplateSpecialization())
77 return false;
79 // Ignore virtual methods, sometimes we want to pass by value, and we cannot tell from
80 // the limited info available at an individual site.
81 const CXXMethodDecl * methodDecl = dyn_cast<CXXMethodDecl>(functionDecl);
82 if (methodDecl && methodDecl->isVirtual())
83 return false;
85 // Only warn on the definition of the function:
86 if (!functionDecl->doesThisDeclarationHaveABody())
87 return false;
89 mbInsideFunctionDecl = true;
90 mParamExclusions.clear();
91 return true;
94 bool PassParamsByRef::PostTraverseFunctionDecl(FunctionDecl* functionDecl, bool)
96 mbInsideFunctionDecl = false;
98 unsigned n = functionDecl->getNumParams();
99 for (unsigned i = 0; i != n; ++i) {
100 const ParmVarDecl * pvDecl = functionDecl->getParamDecl(i);
101 auto const t = pvDecl->getType();
102 if (!isFat(t))
103 continue;
104 if (mParamExclusions.find(pvDecl) != mParamExclusions.end())
105 continue;
106 report(
107 DiagnosticsEngine::Warning,
108 ("passing %0 by value, rather pass by const lvalue reference"),
109 pvDecl->getLocation())
110 << t << pvDecl->getSourceRange();
111 auto can = functionDecl->getCanonicalDecl();
112 if (can->getLocation() != functionDecl->getLocation()) {
113 report(
114 DiagnosticsEngine::Note, "function is declared here:",
115 can->getLocation())
116 << can->getSourceRange();
119 return true;
122 bool PassParamsByRef::TraverseFunctionDecl(FunctionDecl* functionDecl)
124 bool ret = true;
125 if (PreTraverseFunctionDecl(functionDecl))
127 ret = RecursiveASTVisitor::TraverseFunctionDecl(functionDecl);
128 PostTraverseFunctionDecl(functionDecl, ret);
130 return ret;
133 bool PassParamsByRef::PreTraverseCXXMethodDecl(CXXMethodDecl* functionDecl)
135 return PreTraverseFunctionDecl(functionDecl);
138 bool PassParamsByRef::PostTraverseCXXMethodDecl(CXXMethodDecl* functionDecl, bool b)
140 return PostTraverseFunctionDecl(functionDecl, b);
143 bool PassParamsByRef::TraverseCXXMethodDecl(CXXMethodDecl* functionDecl)
145 bool ret = true;
146 if (PreTraverseCXXMethodDecl(functionDecl))
148 ret = RecursiveASTVisitor::TraverseCXXMethodDecl(functionDecl);
149 PostTraverseCXXMethodDecl(functionDecl, ret);
151 return ret;
154 bool PassParamsByRef::PreTraverseCXXConstructorDecl(CXXConstructorDecl* functionDecl)
156 return PreTraverseFunctionDecl(functionDecl);
159 bool PassParamsByRef::PostTraverseCXXConstructorDecl(CXXConstructorDecl* functionDecl, bool b)
161 return PostTraverseFunctionDecl(functionDecl, b);
164 bool PassParamsByRef::TraverseCXXConstructorDecl(CXXConstructorDecl* functionDecl)
166 bool ret = true;
167 if (PreTraverseCXXConstructorDecl(functionDecl))
169 ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(functionDecl);
170 PostTraverseCXXConstructorDecl(functionDecl, ret);
172 return ret;
175 bool PassParamsByRef::PreTraverseImplicitCastExpr(ImplicitCastExpr * expr)
177 if (ignoreLocation(expr))
178 return false;
179 if (expr->getCastKind() == CK_LValueToRValue
180 && isa<DeclRefExpr>(expr->getSubExpr()->IgnoreParenImpCasts()))
181 return false;
182 return true;
185 bool PassParamsByRef::TraverseImplicitCastExpr(ImplicitCastExpr * expr)
187 bool ret = true;
188 if (PreTraverseImplicitCastExpr(expr))
190 ret = RecursiveASTVisitor::TraverseImplicitCastExpr(expr);
192 return ret;
195 bool PassParamsByRef::VisitBinaryOperator(const BinaryOperator * binaryOperator)
197 if (binaryOperator->getOpcode() != BO_Assign) {
198 return true;
200 if (!mbInsideFunctionDecl)
201 return true;
202 // if we are assigning to a parameter, it can be inconvenient to make the param pass-by-ref
203 if (auto declRefExpr = dyn_cast<DeclRefExpr>(binaryOperator->getLHS()))
205 if (auto parmVarDecl = dyn_cast<ParmVarDecl>(declRefExpr->getDecl()))
206 mParamExclusions.emplace(parmVarDecl);
208 return true;
211 bool PassParamsByRef::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr * cxxOperatorCallExpr )
213 if (!mbInsideFunctionDecl)
214 return true;
215 // if we are assigning to a parameter, it can be inconvenient to make the param pass-by-ref
216 auto op = cxxOperatorCallExpr->getOperator();
217 if ( op != clang::OverloadedOperatorKind::OO_Equal
218 && op != clang::OverloadedOperatorKind::OO_SlashEqual
219 && op != clang::OverloadedOperatorKind::OO_StarEqual
220 && op != clang::OverloadedOperatorKind::OO_MinusEqual
221 && op != clang::OverloadedOperatorKind::OO_PlusEqual)
222 return true;
223 auto declRefExpr = dyn_cast<DeclRefExpr>(cxxOperatorCallExpr->getArg(0));
224 if (!declRefExpr)
225 return true;
226 if (auto parmVarDecl = dyn_cast<ParmVarDecl>(declRefExpr->getDecl()))
227 mParamExclusions.emplace(parmVarDecl);
228 return true;
231 bool PassParamsByRef::VisitCallExpr(const CallExpr * callExpr )
233 if (!mbInsideFunctionDecl)
234 return true;
235 if (loplugin::DeclCheck(callExpr->getCalleeDecl()).Function("move").StdNamespace())
236 if (auto declRefExpr = dyn_cast<DeclRefExpr>(callExpr->getArg(0)))
238 if (auto parmVarDecl = dyn_cast<ParmVarDecl>(declRefExpr->getDecl()))
239 mParamExclusions.emplace(parmVarDecl);
241 if (auto const fun = callExpr->getDirectCallee())
243 unsigned const n = std::min(fun->getNumParams(), callExpr->getNumArgs());
244 for (unsigned i = 0; i != n; ++i)
246 if (!loplugin::TypeCheck(fun->getParamDecl(i)->getType())
247 .LvalueReference()
248 .NonConstVolatile())
249 continue;
250 auto a = callExpr->getArg(i)->IgnoreParenImpCasts();
251 if (auto declRefExpr = dyn_cast<DeclRefExpr>(a))
252 if (auto parmVarDecl = dyn_cast<ParmVarDecl>(declRefExpr->getDecl()))
253 mParamExclusions.emplace(parmVarDecl);
256 return true;
259 bool PassParamsByRef::VisitCXXMemberCallExpr(const CXXMemberCallExpr * callExpr )
261 if (!mbInsideFunctionDecl)
262 return true;
263 // exclude cases where we call a non-const method on the parameter i.e. potentially mutating it
264 if (auto const e1 = callExpr->getMethodDecl())
265 if (!e1->isConst())
267 auto a = callExpr->getImplicitObjectArgument()->IgnoreParenImpCasts();
268 if (auto declRefExpr = dyn_cast<DeclRefExpr>(a))
269 if (auto parmVarDecl = dyn_cast<ParmVarDecl>(declRefExpr->getDecl()))
270 mParamExclusions.emplace(parmVarDecl);
272 return true;
275 bool PassParamsByRef::isFat(QualType type) {
276 if (!type->isRecordType()) {
277 return false;
279 loplugin::TypeCheck tc(type);
280 if ((tc.Class("Reference").Namespace("uno").Namespace("star")
281 .Namespace("sun").Namespace("com").GlobalNamespace())
282 || (tc.Class("Sequence").Namespace("uno").Namespace("star")
283 .Namespace("sun").Namespace("com").GlobalNamespace())
284 || tc.Class("OString").Namespace("rtl").GlobalNamespace()
285 || tc.Class("OUString").Namespace("rtl").GlobalNamespace()
286 || tc.Class("Reference").Namespace("rtl").GlobalNamespace())
288 return true;
290 if (type->isIncompleteType()) {
291 return false;
293 clang::Type const * t2 = type.getTypePtrOrNull();
294 return t2 != nullptr
295 && compiler.getASTContext().getTypeSizeInChars(t2).getQuantity() > 64;
298 loplugin::Plugin::Registration< PassParamsByRef > passparamsbyref("passparamsbyref");
300 } // namespace
302 #endif // LO_CLANG_SHARED_PLUGINS
304 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */