1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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
16 #include <unordered_set>
21 class RedundantFCast final
: public loplugin::FilteringPlugin
<RedundantFCast
>
24 explicit RedundantFCast(loplugin::InstantiationData
const& data
)
25 : FilteringPlugin(data
)
29 bool VisitReturnStmt(ReturnStmt
const* returnStmt
)
31 if (ignoreLocation(returnStmt
))
33 Expr
const* expr
= returnStmt
->getRetValue();
36 if (auto exprWithCleanups
= dyn_cast
<ExprWithCleanups
>(expr
))
37 expr
= exprWithCleanups
->getSubExpr();
38 if (auto cxxConstructExpr
= dyn_cast
<CXXConstructExpr
>(expr
))
40 if (cxxConstructExpr
->getNumArgs() != 1)
42 expr
= cxxConstructExpr
->getArg(0);
44 if (auto materializeTemporaryExpr
= dyn_cast
<MaterializeTemporaryExpr
>(expr
))
45 expr
= materializeTemporaryExpr
->getSubExpr();
46 auto cxxFunctionalCastExpr
= dyn_cast
<CXXFunctionalCastExpr
>(expr
);
47 if (!cxxFunctionalCastExpr
)
49 auto const t1
= cxxFunctionalCastExpr
->getTypeAsWritten();
50 auto const t2
= compat::getSubExprAsWritten(cxxFunctionalCastExpr
)->getType();
51 if (t1
.getCanonicalType().getTypePtr() != t2
.getCanonicalType().getTypePtr())
53 if (!loplugin::isOkToRemoveArithmeticCast(compiler
.getASTContext(), t1
, t2
,
54 cxxFunctionalCastExpr
->getSubExpr()))
58 if (m_Seen
.insert(cxxFunctionalCastExpr
->getExprLoc()).second
)
60 if (suppressWarningAt(cxxFunctionalCastExpr
->getBeginLoc()))
64 report(DiagnosticsEngine::Warning
, "redundant functional cast from %0 to %1",
65 cxxFunctionalCastExpr
->getExprLoc())
66 << t2
<< t1
<< cxxFunctionalCastExpr
->getSourceRange();
71 /* Check for the creation of unnecessary temporaries when calling a method that takes a param by const & */
72 bool VisitCallExpr(CallExpr
const* callExpr
)
74 if (ignoreLocation(callExpr
))
76 const FunctionDecl
* functionDecl
;
77 if (isa
<CXXMemberCallExpr
>(callExpr
))
78 functionDecl
= dyn_cast
<CXXMemberCallExpr
>(callExpr
)->getMethodDecl();
80 functionDecl
= callExpr
->getDirectCallee();
84 unsigned len
= std::min(callExpr
->getNumArgs(), functionDecl
->getNumParams());
85 for (unsigned i
= 0; i
< len
; ++i
)
87 // check if param is const&
88 auto param
= functionDecl
->getParamDecl(i
);
89 auto lvalueType
= param
->getType()->getAs
<LValueReferenceType
>();
92 if (!lvalueType
->getPointeeType().isConstQualified())
94 auto paramClassOrStructType
= lvalueType
->getPointeeType()->getAs
<RecordType
>();
95 if (!paramClassOrStructType
)
97 // check for temporary and functional cast in argument expression
98 auto arg
= callExpr
->getArg(i
)->IgnoreImpCasts();
99 auto materializeTemporaryExpr
= dyn_cast
<MaterializeTemporaryExpr
>(arg
);
100 if (!materializeTemporaryExpr
)
102 auto functionalCast
= dyn_cast
<CXXFunctionalCastExpr
>(
103 materializeTemporaryExpr
->getSubExpr()->IgnoreImpCasts());
106 auto const t1
= functionalCast
->getTypeAsWritten();
107 auto const t2
= compat::getSubExprAsWritten(functionalCast
)->getType();
108 if (t1
.getCanonicalType().getTypePtr() != t2
.getCanonicalType().getTypePtr())
110 // Check that the underlying expression is of the same class/struct type as the param i.e. that we are not instantiating
112 if (t1
.getCanonicalType().getTypePtr() != paramClassOrStructType
)
114 // Don't warn about (necessary) cast from braced-init-list in non-deduced contexts:
115 if (lvalueType
->getPointeeType()->getAs
<SubstTemplateTypeParmType
>() != nullptr
116 && loplugin::TypeCheck(t1
).ClassOrStruct("initializer_list").StdNamespace()
117 && isa
<CXXStdInitializerListExpr
>(compat::getSubExprAsWritten(functionalCast
)))
122 if (m_Seen
.insert(arg
->getExprLoc()).second
)
124 report(DiagnosticsEngine::Warning
, "redundant functional cast from %0 to %1",
126 << t2
<< t1
<< arg
->getSourceRange();
127 report(DiagnosticsEngine::Note
, "in call to method here", param
->getLocation())
128 << param
->getSourceRange();
134 /* Check for the creation of unnecessary temporaries when calling a constructor that takes a param by const & */
135 bool VisitCXXConstructExpr(CXXConstructExpr
const* callExpr
)
137 if (ignoreLocation(callExpr
))
139 const CXXConstructorDecl
* functionDecl
= callExpr
->getConstructor();
141 unsigned len
= std::min(callExpr
->getNumArgs(), functionDecl
->getNumParams());
142 for (unsigned i
= 0; i
< len
; ++i
)
144 // check if param is const&
145 auto param
= functionDecl
->getParamDecl(i
);
146 auto rvalueType
= param
->getType()->getAs
<RValueReferenceType
>();
149 auto lvalueType
= param
->getType()->getAs
<LValueReferenceType
>();
152 if (!lvalueType
->getPointeeType().isConstQualified())
155 auto valueType
= param
->getType()->getAs
<ReferenceType
>();
156 auto paramClassOrStructType
= valueType
->getPointeeType()->getAs
<RecordType
>();
157 if (!paramClassOrStructType
)
159 // check for temporary and functional cast in argument expression
160 auto arg
= compat::IgnoreParenImplicit(callExpr
->getArg(i
));
161 auto functionalCast
= dyn_cast
<CXXFunctionalCastExpr
>(arg
);
164 auto const t1
= functionalCast
->getTypeAsWritten();
165 auto const t2
= compat::getSubExprAsWritten(functionalCast
)->getType();
166 if (t1
.getCanonicalType().getTypePtr() != t2
.getCanonicalType().getTypePtr())
168 // Check that the underlying expression is of the same class/struct type as the param i.e. that we are not instantiating
170 if (t1
.getCanonicalType().getTypePtr() != paramClassOrStructType
)
174 // constructing a temporary to pass to a && argument is fine. But we will see that in the VisitFunctionalCast
175 // method below and generate a warning. And we don't have enough context there to determine that we're
176 // doing the wrong thing. So add the expression to the m_Seen list here to prevent that warning.
177 m_Seen
.insert(functionalCast
->getExprLoc());
181 if (m_Seen
.insert(arg
->getExprLoc()).second
)
183 if (suppressWarningAt(arg
->getBeginLoc()))
187 report(DiagnosticsEngine::Warning
,
188 "redundant functional cast from %0 to %1 in construct expression",
190 << t2
<< t1
<< arg
->getSourceRange();
191 report(DiagnosticsEngine::Note
, "in call to method here", param
->getLocation())
192 << param
->getSourceRange();
198 // Find redundant cast to std::function, where clang reports
199 // two different types for the inner and outer
200 bool isRedundantStdFunctionCast(CXXFunctionalCastExpr
const* expr
)
202 bool deduced
= false;
204 auto const written
= expr
->getTypeAsWritten();
205 if (auto const t1
= written
->getAs
<DeducedTemplateSpecializationType
>())
207 auto const decl
= t1
->getTemplateName().getAsTemplateDecl();
212 if (!loplugin::DeclCheck(decl
->getTemplatedDecl())
213 .ClassOrStruct("function")
220 else if (auto const t2
= written
->getAs
<TemplateSpecializationType
>())
222 auto const decl
= t2
->getTemplateName().getAsTemplateDecl();
227 if (!loplugin::DeclCheck(decl
->getTemplatedDecl())
228 .ClassOrStruct("function")
233 auto const args
= t2
->template_arguments();
234 if (args
.size() != 1)
238 report(DiagnosticsEngine::Fatal
,
239 "TODO: unexpected std::function with %0 template arguments",
241 << compat::diagnosticSize(args
.size()) << expr
->getSourceRange();
245 if (args
[0].getKind() != TemplateArgument::Type
)
249 report(DiagnosticsEngine::Fatal
,
250 "TODO: unexpected std::function with non-type template argument",
252 << expr
->getSourceRange();
256 target
= args
[0].getAsType();
263 = dyn_cast
<CXXConstructExpr
>(compat::IgnoreParenImplicit(expr
->getSubExpr()));
267 = dyn_cast
<LambdaExpr
>(compat::IgnoreParenImplicit(cxxConstruct
->getArg(0)));
271 // std::function([...](Args)->Ret{...}) should always be redundant:
273 auto const decl
= lambda
->getCallOperator();
274 std::vector
<QualType
> args
;
275 for (unsigned i
= 0; i
!= decl
->getNumParams(); ++i
)
277 args
.push_back(decl
->getParamDecl(i
)->getType());
280 = compiler
.getASTContext().getFunctionType(decl
->getReturnType(), args
, {});
281 // std::function<Ret1(Args1)>([...](Args2)->Ret2{...}) is redundant if target Ret1(Args1)
282 // matches source Ret2(Args2):
283 return target
.getCanonicalType() == source
.getCanonicalType();
286 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
const* expr
)
288 if (ignoreLocation(expr
))
290 // specifying the name for an init-list is necessary sometimes
291 if (isa
<InitListExpr
>(compat::IgnoreParenImplicit(expr
->getSubExpr())))
293 if (isa
<CXXStdInitializerListExpr
>(compat::IgnoreParenImplicit(expr
->getSubExpr())))
295 auto const t1
= expr
->getTypeAsWritten();
296 auto const t2
= compat::getSubExprAsWritten(expr
)->getType();
297 if (!(t1
.getCanonicalType().getTypePtr() == t2
.getCanonicalType().getTypePtr()
298 || isRedundantStdFunctionCast(expr
)))
302 // (a) we do a lot of int/sal_Int32 kind of casts which might be platform necessary?
303 // (b) we do bool/bool casts in unit tests to avoid one of the other plugins
304 // so just ignore this kind of thing for now
305 if (const auto* BT
= dyn_cast
<BuiltinType
>(t1
->getUnqualifiedDesugaredType()))
307 auto k
= BT
->getKind();
308 if (k
== BuiltinType::Double
|| k
== BuiltinType::Float
309 || (k
>= BuiltinType::Bool
&& k
<= BuiltinType::Int128
))
312 if (const auto* BT
= dyn_cast
<BuiltinType
>(t2
->getUnqualifiedDesugaredType()))
314 auto k
= BT
->getKind();
315 if (k
== BuiltinType::Double
|| k
== BuiltinType::Float
316 || (k
>= BuiltinType::Bool
&& k
<= BuiltinType::Int128
))
319 auto tc
= loplugin::TypeCheck(t1
);
320 if (tc
.Typedef("sal_Int32").GlobalNamespace())
323 if (m_Seen
.insert(expr
->getExprLoc()).second
)
325 if (suppressWarningAt(expr
->getBeginLoc()))
329 report(DiagnosticsEngine::Warning
, "redundant functional cast from %0 to %1",
331 << t2
<< t1
<< expr
->getSourceRange();
336 bool preRun() override
338 if (!compiler
.getLangOpts().CPlusPlus
)
346 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
349 std::unordered_set
<SourceLocation
> m_Seen
;
352 static loplugin::Plugin::Registration
<RedundantFCast
> redundantfcast("redundantfcast");
356 #endif // LO_CLANG_SHARED_PLUGINS
358 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */