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
= compat::getSubExpr(materializeTemporaryExpr
);
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
)
59 report(DiagnosticsEngine::Warning
, "redundant functional cast from %0 to %1",
60 cxxFunctionalCastExpr
->getExprLoc())
61 << t2
<< t1
<< cxxFunctionalCastExpr
->getSourceRange();
65 /* Check for the creation of unnecessary temporaries when calling a method that takes a param by const & */
66 bool VisitCallExpr(CallExpr
const* callExpr
)
68 if (ignoreLocation(callExpr
))
70 const FunctionDecl
* functionDecl
;
71 if (isa
<CXXMemberCallExpr
>(callExpr
))
72 functionDecl
= dyn_cast
<CXXMemberCallExpr
>(callExpr
)->getMethodDecl();
74 functionDecl
= callExpr
->getDirectCallee();
78 unsigned len
= std::min(callExpr
->getNumArgs(), functionDecl
->getNumParams());
79 for (unsigned i
= 0; i
< len
; ++i
)
81 // check if param is const&
82 auto param
= functionDecl
->getParamDecl(i
);
83 auto lvalueType
= param
->getType()->getAs
<LValueReferenceType
>();
86 if (!lvalueType
->getPointeeType().isConstQualified())
88 auto paramClassOrStructType
= lvalueType
->getPointeeType()->getAs
<RecordType
>();
89 if (!paramClassOrStructType
)
91 // check for temporary and functional cast in argument expression
92 auto arg
= callExpr
->getArg(i
)->IgnoreImpCasts();
93 auto materializeTemporaryExpr
= dyn_cast
<MaterializeTemporaryExpr
>(arg
);
94 if (!materializeTemporaryExpr
)
96 auto functionalCast
= dyn_cast
<CXXFunctionalCastExpr
>(
97 compat::getSubExpr(materializeTemporaryExpr
)->IgnoreImpCasts());
100 auto const t1
= functionalCast
->getTypeAsWritten();
101 auto const t2
= compat::getSubExprAsWritten(functionalCast
)->getType();
102 if (t1
.getCanonicalType().getTypePtr() != t2
.getCanonicalType().getTypePtr())
104 // Check that the underlying expression is of the same class/struct type as the param i.e. that we are not instantiating
106 if (t1
.getCanonicalType().getTypePtr() != paramClassOrStructType
)
108 // Don't warn about (necessary) cast from braced-init-list in non-deduced contexts:
109 if (lvalueType
->getPointeeType()->getAs
<SubstTemplateTypeParmType
>() != nullptr
110 && loplugin::TypeCheck(t1
).ClassOrStruct("initializer_list").StdNamespace()
111 && isa
<CXXStdInitializerListExpr
>(compat::getSubExprAsWritten(functionalCast
)))
116 if (m_Seen
.insert(arg
->getExprLoc()).second
)
118 report(DiagnosticsEngine::Warning
, "redundant functional cast from %0 to %1",
120 << t2
<< t1
<< arg
->getSourceRange();
121 report(DiagnosticsEngine::Note
, "in call to method here", param
->getLocation())
122 << param
->getSourceRange();
128 /* Check for the creation of unnecessary temporaries when calling a constructor that takes a param by const & */
129 bool VisitCXXConstructExpr(CXXConstructExpr
const* callExpr
)
131 if (ignoreLocation(callExpr
))
133 const CXXConstructorDecl
* functionDecl
= callExpr
->getConstructor();
135 unsigned len
= std::min(callExpr
->getNumArgs(), functionDecl
->getNumParams());
136 for (unsigned i
= 0; i
< len
; ++i
)
138 // check if param is const&
139 auto param
= functionDecl
->getParamDecl(i
);
140 auto lvalueType
= param
->getType()->getAs
<LValueReferenceType
>();
143 if (!lvalueType
->getPointeeType().isConstQualified())
145 auto paramClassOrStructType
= lvalueType
->getPointeeType()->getAs
<RecordType
>();
146 if (!paramClassOrStructType
)
148 // check for temporary and functional cast in argument expression
149 auto arg
= callExpr
->getArg(i
)->IgnoreImplicit();
150 auto functionalCast
= dyn_cast
<CXXFunctionalCastExpr
>(arg
);
153 auto const t1
= functionalCast
->getTypeAsWritten();
154 auto const t2
= compat::getSubExprAsWritten(functionalCast
)->getType();
155 if (t1
.getCanonicalType().getTypePtr() != t2
.getCanonicalType().getTypePtr())
157 // Check that the underlying expression is of the same class/struct type as the param i.e. that we are not instantiating
159 if (t1
.getCanonicalType().getTypePtr() != paramClassOrStructType
)
162 if (m_Seen
.insert(arg
->getExprLoc()).second
)
164 report(DiagnosticsEngine::Warning
, "redundant functional cast from %0 to %1",
166 << t2
<< t1
<< arg
->getSourceRange();
167 report(DiagnosticsEngine::Note
, "in call to method here", param
->getLocation())
168 << param
->getSourceRange();
174 // Find redundant cast to std::function, where clang reports
175 // two different types for the inner and outer
176 bool isRedundantStdFunctionCast(CXXFunctionalCastExpr
const* expr
)
178 bool deduced
= false;
180 auto const written
= expr
->getTypeAsWritten();
181 if (auto const t1
= written
->getAs
<DeducedTemplateSpecializationType
>())
183 auto const decl
= t1
->getTemplateName().getAsTemplateDecl();
188 if (!loplugin::DeclCheck(decl
->getTemplatedDecl())
189 .ClassOrStruct("function")
196 else if (auto const t2
= written
->getAs
<TemplateSpecializationType
>())
198 auto const decl
= t2
->getTemplateName().getAsTemplateDecl();
203 if (!loplugin::DeclCheck(decl
->getTemplatedDecl())
204 .ClassOrStruct("function")
209 if (t2
->getNumArgs() != 1)
213 report(DiagnosticsEngine::Fatal
,
214 "TODO: unexpected std::function with %0 template arguments",
216 << t2
->getNumArgs() << expr
->getSourceRange();
220 if (t2
->getArg(0).getKind() != TemplateArgument::Type
)
224 report(DiagnosticsEngine::Fatal
,
225 "TODO: unexpected std::function with non-type template argument",
227 << expr
->getSourceRange();
231 target
= t2
->getArg(0).getAsType();
237 auto cxxConstruct
= dyn_cast
<CXXConstructExpr
>(compat::IgnoreImplicit(expr
->getSubExpr()));
240 auto const lambda
= dyn_cast
<LambdaExpr
>(cxxConstruct
->getArg(0));
244 // std::function([...](Args)->Ret{...}) should always be redundant:
246 auto const decl
= lambda
->getCallOperator();
247 std::vector
<QualType
> args
;
248 for (unsigned i
= 0; i
!= decl
->getNumParams(); ++i
)
250 args
.push_back(decl
->getParamDecl(i
)->getType());
253 = compiler
.getASTContext().getFunctionType(decl
->getReturnType(), args
, {});
254 // std::function<Ret1(Args1)>([...](Args2)->Ret2{...}) is redundant if target Ret1(Args1)
255 // matches source Ret2(Args2):
256 return target
.getCanonicalType() == source
.getCanonicalType();
259 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
const* expr
)
261 if (ignoreLocation(expr
))
263 // specifying the name for an init-list is necessary sometimes
264 if (isa
<InitListExpr
>(expr
->getSubExpr()->IgnoreImplicit()))
266 if (isa
<CXXStdInitializerListExpr
>(expr
->getSubExpr()->IgnoreImplicit()))
268 auto const t1
= expr
->getTypeAsWritten();
269 auto const t2
= compat::getSubExprAsWritten(expr
)->getType();
270 if (!(t1
.getCanonicalType().getTypePtr() == t2
.getCanonicalType().getTypePtr()
271 || isRedundantStdFunctionCast(expr
)))
275 // (a) we do a lot of int/sal_Int32 kind of casts which might be platform necessary?
276 // (b) we do bool/bool casts in unit tests to avoid one of the other plugins
277 // so just ignore this kind of thing for now
278 if (const auto* BT
= dyn_cast
<BuiltinType
>(t1
->getUnqualifiedDesugaredType()))
280 auto k
= BT
->getKind();
281 if (k
== BuiltinType::Double
|| k
== BuiltinType::Float
282 || (k
>= BuiltinType::Bool
&& k
<= BuiltinType::Int128
))
285 if (const auto* BT
= dyn_cast
<BuiltinType
>(t2
->getUnqualifiedDesugaredType()))
287 auto k
= BT
->getKind();
288 if (k
== BuiltinType::Double
|| k
== BuiltinType::Float
289 || (k
>= BuiltinType::Bool
&& k
<= BuiltinType::Int128
))
292 auto tc
= loplugin::TypeCheck(t1
);
293 if (tc
.Typedef("sal_Int32").GlobalNamespace())
296 if (m_Seen
.insert(expr
->getExprLoc()).second
)
297 report(DiagnosticsEngine::Warning
, "redundant functional cast from %0 to %1",
299 << t2
<< t1
<< expr
->getSourceRange();
300 //getParentStmt(expr)->dump();
304 bool preRun() override
306 if (!compiler
.getLangOpts().CPlusPlus
)
308 std::string fn
= handler
.getMainFileName().str();
309 loplugin::normalizeDotDotInFilePath(fn
);
310 // necessary on some other platforms
311 if (fn
== SRCDIR
"/sal/osl/unx/socket.cxx")
313 // compile-time check of constant
314 if (fn
== SRCDIR
"/bridges/source/jni_uno/jni_bridge.cxx")
322 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
325 std::unordered_set
<SourceLocation
> m_Seen
;
328 static loplugin::Plugin::Registration
<RedundantFCast
> redundantfcast("redundantfcast");
332 #endif // LO_CLANG_SHARED_PLUGINS
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */