1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
9 #ifndef LO_CLANG_SHARED_PLUGINS
12 #include <unordered_set>
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.
31 class PassParamsByRef
:
32 public loplugin::FilteringPlugin
<PassParamsByRef
>
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
*);
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
))
74 if (functionDecl
->isDeleted()
75 || functionDecl
->isFunctionTemplateSpecialization())
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())
85 // Only warn on the definition of the function:
86 if (!functionDecl
->doesThisDeclarationHaveABody())
89 mbInsideFunctionDecl
= true;
90 mParamExclusions
.clear();
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();
104 if (mParamExclusions
.find(pvDecl
) != mParamExclusions
.end())
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()) {
114 DiagnosticsEngine::Note
, "function is declared here:",
116 << can
->getSourceRange();
122 bool PassParamsByRef::TraverseFunctionDecl(FunctionDecl
* functionDecl
)
125 if (PreTraverseFunctionDecl(functionDecl
))
127 ret
= RecursiveASTVisitor::TraverseFunctionDecl(functionDecl
);
128 PostTraverseFunctionDecl(functionDecl
, 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
)
146 if (PreTraverseCXXMethodDecl(functionDecl
))
148 ret
= RecursiveASTVisitor::TraverseCXXMethodDecl(functionDecl
);
149 PostTraverseCXXMethodDecl(functionDecl
, 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
)
167 if (PreTraverseCXXConstructorDecl(functionDecl
))
169 ret
= RecursiveASTVisitor::TraverseCXXConstructorDecl(functionDecl
);
170 PostTraverseCXXConstructorDecl(functionDecl
, ret
);
175 bool PassParamsByRef::PreTraverseImplicitCastExpr(ImplicitCastExpr
* expr
)
177 if (ignoreLocation(expr
))
179 if (expr
->getCastKind() == CK_LValueToRValue
180 && isa
<DeclRefExpr
>(expr
->getSubExpr()->IgnoreParenImpCasts()))
185 bool PassParamsByRef::TraverseImplicitCastExpr(ImplicitCastExpr
* expr
)
188 if (PreTraverseImplicitCastExpr(expr
))
190 ret
= RecursiveASTVisitor::TraverseImplicitCastExpr(expr
);
195 bool PassParamsByRef::VisitBinaryOperator(const BinaryOperator
* binaryOperator
)
197 if (binaryOperator
->getOpcode() != BO_Assign
) {
200 if (!mbInsideFunctionDecl
)
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
);
211 bool PassParamsByRef::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr
* cxxOperatorCallExpr
)
213 if (!mbInsideFunctionDecl
)
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
)
223 auto declRefExpr
= dyn_cast
<DeclRefExpr
>(cxxOperatorCallExpr
->getArg(0));
226 if (auto parmVarDecl
= dyn_cast
<ParmVarDecl
>(declRefExpr
->getDecl()))
227 mParamExclusions
.emplace(parmVarDecl
);
231 bool PassParamsByRef::VisitCallExpr(const CallExpr
* callExpr
)
233 if (!mbInsideFunctionDecl
)
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())
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
);
259 bool PassParamsByRef::VisitCXXMemberCallExpr(const CXXMemberCallExpr
* callExpr
)
261 if (!mbInsideFunctionDecl
)
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())
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
);
275 bool PassParamsByRef::isFat(QualType type
) {
276 if (!type
->isRecordType()) {
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())
290 if (type
->isIncompleteType()) {
293 clang::Type
const * t2
= type
.getTypePtrOrNull();
295 && compiler
.getASTContext().getTypeSizeInChars(t2
).getQuantity() > 64;
298 loplugin::Plugin::Registration
< PassParamsByRef
> passparamsbyref("passparamsbyref");
302 #endif // LO_CLANG_SHARED_PLUGINS
304 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */