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/.
18 class RedundantFCast final
: public loplugin::FilteringPlugin
<RedundantFCast
>
21 explicit RedundantFCast(loplugin::InstantiationData
const& data
)
22 : FilteringPlugin(data
)
26 bool TraverseFunctionDecl(FunctionDecl
* functionDecl
)
28 auto prev
= m_CurrentFunctionDecl
;
29 m_CurrentFunctionDecl
= functionDecl
;
30 auto rv
= RecursiveASTVisitor
<RedundantFCast
>::TraverseFunctionDecl(functionDecl
);
31 m_CurrentFunctionDecl
= prev
;
35 bool VisitReturnStmt(ReturnStmt
const* returnStmt
)
37 if (ignoreLocation(returnStmt
))
39 Expr
const* expr
= returnStmt
->getRetValue();
42 if (auto exprWithCleanups
= dyn_cast
<ExprWithCleanups
>(expr
))
43 expr
= exprWithCleanups
->getSubExpr();
44 if (auto cxxConstructExpr
= dyn_cast
<CXXConstructExpr
>(expr
))
46 if (cxxConstructExpr
->getNumArgs() != 1)
48 expr
= cxxConstructExpr
->getArg(0);
50 if (auto materializeTemporaryExpr
= dyn_cast
<MaterializeTemporaryExpr
>(expr
))
51 expr
= materializeTemporaryExpr
->GetTemporaryExpr();
52 auto cxxFunctionalCastExpr
= dyn_cast
<CXXFunctionalCastExpr
>(expr
);
53 if (!cxxFunctionalCastExpr
)
55 auto const t1
= cxxFunctionalCastExpr
->getTypeAsWritten();
56 auto const t2
= compat::getSubExprAsWritten(cxxFunctionalCastExpr
)->getType();
57 if (t1
.getCanonicalType().getTypePtr() != t2
.getCanonicalType().getTypePtr())
59 if (!loplugin::isOkToRemoveArithmeticCast(compiler
.getASTContext(), t1
, t2
,
60 cxxFunctionalCastExpr
->getSubExpr()))
64 report(DiagnosticsEngine::Warning
, "redundant functional cast from %0 to %1",
65 cxxFunctionalCastExpr
->getExprLoc())
66 << t2
<< t1
<< cxxFunctionalCastExpr
->getSourceRange();
70 /* Check for the creation of unnecessary temporaries when calling a method that takes a param by const & */
71 bool VisitCallExpr(CallExpr
const* callExpr
)
73 if (ignoreLocation(callExpr
))
75 const FunctionDecl
* functionDecl
;
76 if (isa
<CXXMemberCallExpr
>(callExpr
))
77 functionDecl
= dyn_cast
<CXXMemberCallExpr
>(callExpr
)->getMethodDecl();
79 functionDecl
= callExpr
->getDirectCallee();
83 unsigned len
= std::min(callExpr
->getNumArgs(), functionDecl
->getNumParams());
84 for (unsigned i
= 0; i
< len
; ++i
)
86 // check if param is const&
87 auto param
= functionDecl
->getParamDecl(i
);
88 auto lvalueType
= param
->getType()->getAs
<LValueReferenceType
>();
91 if (!lvalueType
->getPointeeType().isConstQualified())
93 auto paramClassOrStructType
= lvalueType
->getPointeeType()->getAs
<RecordType
>();
94 if (!paramClassOrStructType
)
96 // check for temporary and functional cast in argument expression
97 auto arg
= callExpr
->getArg(i
)->IgnoreImpCasts();
98 auto materializeTemporaryExpr
= dyn_cast
<MaterializeTemporaryExpr
>(arg
);
99 if (!materializeTemporaryExpr
)
101 auto functionalCast
= dyn_cast
<CXXFunctionalCastExpr
>(
102 materializeTemporaryExpr
->GetTemporaryExpr()->IgnoreImpCasts());
105 auto const t1
= functionalCast
->getTypeAsWritten();
106 auto const t2
= compat::getSubExprAsWritten(functionalCast
)->getType();
107 if (t1
.getCanonicalType().getTypePtr() != t2
.getCanonicalType().getTypePtr())
109 // Check that the underlying expression is of the same class/struct type as the param i.e. that we are not instantiating
111 if (t1
.getCanonicalType().getTypePtr() != paramClassOrStructType
)
114 report(DiagnosticsEngine::Warning
, "redundant functional cast from %0 to %1",
116 << t2
<< t1
<< arg
->getSourceRange();
117 report(DiagnosticsEngine::Note
, "in call to method here", param
->getLocation())
118 << param
->getSourceRange();
123 /* Check for the creation of unnecessary temporaries when calling a constructor that takes a param by const & */
124 bool VisitCXXConstructExpr(CXXConstructExpr
const* callExpr
)
126 if (ignoreLocation(callExpr
))
128 const CXXConstructorDecl
* functionDecl
= callExpr
->getConstructor();
130 unsigned len
= std::min(callExpr
->getNumArgs(), functionDecl
->getNumParams());
131 for (unsigned i
= 0; i
< len
; ++i
)
133 // check if param is const&
134 auto param
= functionDecl
->getParamDecl(i
);
135 auto lvalueType
= param
->getType()->getAs
<LValueReferenceType
>();
138 if (!lvalueType
->getPointeeType().isConstQualified())
140 auto paramClassOrStructType
= lvalueType
->getPointeeType()->getAs
<RecordType
>();
141 if (!paramClassOrStructType
)
143 // check for temporary and functional cast in argument expression
144 auto arg
= callExpr
->getArg(i
)->IgnoreImplicit();
145 auto functionalCast
= dyn_cast
<CXXFunctionalCastExpr
>(arg
);
148 auto const t1
= functionalCast
->getTypeAsWritten();
149 auto const t2
= compat::getSubExprAsWritten(functionalCast
)->getType();
150 if (t1
.getCanonicalType().getTypePtr() != t2
.getCanonicalType().getTypePtr())
152 // Check that the underlying expression is of the same class/struct type as the param i.e. that we are not instantiating
154 if (t1
.getCanonicalType().getTypePtr() != paramClassOrStructType
)
157 report(DiagnosticsEngine::Warning
, "redundant functional cast from %0 to %1",
159 << t2
<< t1
<< arg
->getSourceRange();
160 report(DiagnosticsEngine::Note
, "in call to method here", param
->getLocation())
161 << param
->getSourceRange();
166 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
const* expr
)
168 if (ignoreLocation(expr
))
170 // specifying the name for an init-list is necessary sometimes
171 if (isa
<InitListExpr
>(expr
->getSubExpr()->IgnoreImplicit()))
173 if (isa
<CXXStdInitializerListExpr
>(expr
->getSubExpr()->IgnoreImplicit()))
175 auto const t1
= expr
->getTypeAsWritten();
176 auto const t2
= compat::getSubExprAsWritten(expr
)->getType();
177 if (t1
.getCanonicalType().getTypePtr() != t2
.getCanonicalType().getTypePtr())
181 // (a) we do a lot of int/sal_Int32 kind of casts which might be platform necessary?
182 // (b) we do bool/bool casts in unit tests to avoid one of the other plugins
183 // so just ignore this kind of thing for now
184 if (const auto* BT
= dyn_cast
<BuiltinType
>(t1
->getUnqualifiedDesugaredType()))
186 auto k
= BT
->getKind();
187 if (k
== BuiltinType::Double
|| k
== BuiltinType::Float
188 || (k
>= BuiltinType::Bool
&& k
<= BuiltinType::Int128
))
191 if (const auto* BT
= dyn_cast
<BuiltinType
>(t2
->getUnqualifiedDesugaredType()))
193 auto k
= BT
->getKind();
194 if (k
== BuiltinType::Double
|| k
== BuiltinType::Float
195 || (k
>= BuiltinType::Bool
&& k
<= BuiltinType::Int128
))
198 auto tc
= loplugin::TypeCheck(t1
);
199 if (tc
.Typedef("sal_Int32").GlobalNamespace())
202 report(DiagnosticsEngine::Warning
, "redundant functional cast from %0 to %1",
204 << t2
<< t1
<< expr
->getSourceRange();
205 //getParentStmt(expr)->dump();
212 if (!compiler
.getLangOpts().CPlusPlus
)
214 std::string fn
= handler
.getMainFileName();
215 loplugin::normalizeDotDotInFilePath(fn
);
216 // necessary on some other platforms
217 if (fn
== SRCDIR
"/sal/osl/unx/socket.cxx")
219 // compile-time check of constant
220 if (fn
== SRCDIR
"/bridges/source/jni_uno/jni_bridge.cxx")
222 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
224 FunctionDecl
const* m_CurrentFunctionDecl
;
227 static loplugin::Plugin::Registration
<RedundantFCast
> reg("redundantfcast");
230 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */