Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / compilerplugins / clang / redundantfcast.cxx
blob5e74b22fe937d75720d4a613bf62ad77788cd0a6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 "check.hxx"
12 #include "compat.hxx"
13 #include "plugin.hxx"
14 #include <iostream>
15 #include <fstream>
16 #include <unordered_set>
17 #include <vector>
19 namespace
21 class RedundantFCast final : public loplugin::FilteringPlugin<RedundantFCast>
23 public:
24 explicit RedundantFCast(loplugin::InstantiationData const& data)
25 : FilteringPlugin(data)
29 bool VisitReturnStmt(ReturnStmt const* returnStmt)
31 if (ignoreLocation(returnStmt))
32 return true;
33 Expr const* expr = returnStmt->getRetValue();
34 if (!expr)
35 return true;
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)
41 return true;
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)
48 return true;
49 auto const t1 = cxxFunctionalCastExpr->getTypeAsWritten();
50 auto const t2 = compat::getSubExprAsWritten(cxxFunctionalCastExpr)->getType();
51 if (t1.getCanonicalType().getTypePtr() != t2.getCanonicalType().getTypePtr())
52 return true;
53 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2,
54 cxxFunctionalCastExpr->getSubExpr()))
56 return true;
58 if (m_Seen.insert(cxxFunctionalCastExpr->getExprLoc()).second)
60 if (suppressWarningAt(cxxFunctionalCastExpr->getBeginLoc()))
62 return true;
64 report(DiagnosticsEngine::Warning, "redundant functional cast from %0 to %1",
65 cxxFunctionalCastExpr->getExprLoc())
66 << t2 << t1 << cxxFunctionalCastExpr->getSourceRange();
68 return true;
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))
75 return true;
76 const FunctionDecl* functionDecl;
77 if (isa<CXXMemberCallExpr>(callExpr))
78 functionDecl = dyn_cast<CXXMemberCallExpr>(callExpr)->getMethodDecl();
79 else
80 functionDecl = callExpr->getDirectCallee();
81 if (!functionDecl)
82 return true;
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>();
90 if (!lvalueType)
91 continue;
92 if (!lvalueType->getPointeeType().isConstQualified())
93 continue;
94 auto paramClassOrStructType = lvalueType->getPointeeType()->getAs<RecordType>();
95 if (!paramClassOrStructType)
96 continue;
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)
101 continue;
102 auto functionalCast = dyn_cast<CXXFunctionalCastExpr>(
103 materializeTemporaryExpr->getSubExpr()->IgnoreImpCasts());
104 if (!functionalCast)
105 continue;
106 auto const t1 = functionalCast->getTypeAsWritten();
107 auto const t2 = compat::getSubExprAsWritten(functionalCast)->getType();
108 if (t1.getCanonicalType().getTypePtr() != t2.getCanonicalType().getTypePtr())
109 continue;
110 // Check that the underlying expression is of the same class/struct type as the param i.e. that we are not instantiating
111 // something useful
112 if (t1.getCanonicalType().getTypePtr() != paramClassOrStructType)
113 continue;
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)))
119 continue;
122 if (m_Seen.insert(arg->getExprLoc()).second)
124 report(DiagnosticsEngine::Warning, "redundant functional cast from %0 to %1",
125 arg->getExprLoc())
126 << t2 << t1 << arg->getSourceRange();
127 report(DiagnosticsEngine::Note, "in call to method here", param->getLocation())
128 << param->getSourceRange();
131 return true;
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))
138 return true;
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>();
147 if (!rvalueType)
149 auto lvalueType = param->getType()->getAs<LValueReferenceType>();
150 if (!lvalueType)
151 continue;
152 if (!lvalueType->getPointeeType().isConstQualified())
153 continue;
155 auto valueType = param->getType()->getAs<ReferenceType>();
156 auto paramClassOrStructType = valueType->getPointeeType()->getAs<RecordType>();
157 if (!paramClassOrStructType)
158 continue;
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);
162 if (!functionalCast)
163 continue;
164 auto const t1 = functionalCast->getTypeAsWritten();
165 auto const t2 = compat::getSubExprAsWritten(functionalCast)->getType();
166 if (t1.getCanonicalType().getTypePtr() != t2.getCanonicalType().getTypePtr())
167 continue;
168 // Check that the underlying expression is of the same class/struct type as the param i.e. that we are not instantiating
169 // something useful
170 if (t1.getCanonicalType().getTypePtr() != paramClassOrStructType)
171 continue;
172 if (rvalueType)
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());
178 continue;
181 if (m_Seen.insert(arg->getExprLoc()).second)
183 if (suppressWarningAt(arg->getBeginLoc()))
185 continue;
187 report(DiagnosticsEngine::Warning,
188 "redundant functional cast from %0 to %1 in construct expression",
189 arg->getExprLoc())
190 << t2 << t1 << arg->getSourceRange();
191 report(DiagnosticsEngine::Note, "in call to method here", param->getLocation())
192 << param->getSourceRange();
195 return true;
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;
203 QualType target;
204 auto const written = expr->getTypeAsWritten();
205 if (auto const t1 = written->getAs<DeducedTemplateSpecializationType>())
207 auto const decl = t1->getTemplateName().getAsTemplateDecl();
208 if (!decl)
210 return false;
212 if (!loplugin::DeclCheck(decl->getTemplatedDecl())
213 .ClassOrStruct("function")
214 .StdNamespace())
216 return false;
218 deduced = true;
220 else if (auto const t2 = written->getAs<TemplateSpecializationType>())
222 auto const decl = t2->getTemplateName().getAsTemplateDecl();
223 if (!decl)
225 return false;
227 if (!loplugin::DeclCheck(decl->getTemplatedDecl())
228 .ClassOrStruct("function")
229 .StdNamespace())
231 return false;
233 auto const args = t2->template_arguments();
234 if (args.size() != 1)
236 if (isDebugMode())
238 report(DiagnosticsEngine::Fatal,
239 "TODO: unexpected std::function with %0 template arguments",
240 expr->getExprLoc())
241 << compat::diagnosticSize(args.size()) << expr->getSourceRange();
243 return false;
245 if (args[0].getKind() != TemplateArgument::Type)
247 if (isDebugMode())
249 report(DiagnosticsEngine::Fatal,
250 "TODO: unexpected std::function with non-type template argument",
251 expr->getExprLoc())
252 << expr->getSourceRange();
254 return false;
256 target = args[0].getAsType();
258 else
260 return false;
262 auto cxxConstruct
263 = dyn_cast<CXXConstructExpr>(compat::IgnoreParenImplicit(expr->getSubExpr()));
264 if (!cxxConstruct)
265 return false;
266 auto const lambda
267 = dyn_cast<LambdaExpr>(compat::IgnoreParenImplicit(cxxConstruct->getArg(0)));
268 if (!lambda)
269 return false;
270 if (deduced)
271 // std::function([...](Args)->Ret{...}) should always be redundant:
272 return true;
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());
279 auto const source
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))
289 return true;
290 // specifying the name for an init-list is necessary sometimes
291 if (isa<InitListExpr>(compat::IgnoreParenImplicit(expr->getSubExpr())))
292 return true;
293 if (isa<CXXStdInitializerListExpr>(compat::IgnoreParenImplicit(expr->getSubExpr())))
294 return true;
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)))
300 return true;
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))
310 return true;
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))
317 return true;
319 auto tc = loplugin::TypeCheck(t1);
320 if (tc.Typedef("sal_Int32").GlobalNamespace())
321 return true;
323 if (m_Seen.insert(expr->getExprLoc()).second)
325 if (suppressWarningAt(expr->getBeginLoc()))
327 return true;
329 report(DiagnosticsEngine::Warning, "redundant functional cast from %0 to %1",
330 expr->getExprLoc())
331 << t2 << t1 << expr->getSourceRange();
333 return true;
336 bool preRun() override
338 if (!compiler.getLangOpts().CPlusPlus)
339 return false;
340 return true;
343 void run() override
345 if (preRun())
346 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
349 std::unordered_set<SourceLocation> m_Seen;
352 static loplugin::Plugin::Registration<RedundantFCast> redundantfcast("redundantfcast");
354 } // namespace
356 #endif // LO_CLANG_SHARED_PLUGINS
358 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */