[en_US] Added 13 autocorrect words
[LibreOffice.git] / compilerplugins / clang / redundantfcast.cxx
blobd600aa5b79df2aa2b2a7c3f9ddc8355c12e01271
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 = compat::getSubExpr(materializeTemporaryExpr);
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)
59 report(DiagnosticsEngine::Warning, "redundant functional cast from %0 to %1",
60 cxxFunctionalCastExpr->getExprLoc())
61 << t2 << t1 << cxxFunctionalCastExpr->getSourceRange();
62 return true;
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))
69 return true;
70 const FunctionDecl* functionDecl;
71 if (isa<CXXMemberCallExpr>(callExpr))
72 functionDecl = dyn_cast<CXXMemberCallExpr>(callExpr)->getMethodDecl();
73 else
74 functionDecl = callExpr->getDirectCallee();
75 if (!functionDecl)
76 return true;
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>();
84 if (!lvalueType)
85 continue;
86 if (!lvalueType->getPointeeType().isConstQualified())
87 continue;
88 auto paramClassOrStructType = lvalueType->getPointeeType()->getAs<RecordType>();
89 if (!paramClassOrStructType)
90 continue;
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)
95 continue;
96 auto functionalCast = dyn_cast<CXXFunctionalCastExpr>(
97 compat::getSubExpr(materializeTemporaryExpr)->IgnoreImpCasts());
98 if (!functionalCast)
99 continue;
100 auto const t1 = functionalCast->getTypeAsWritten();
101 auto const t2 = compat::getSubExprAsWritten(functionalCast)->getType();
102 if (t1.getCanonicalType().getTypePtr() != t2.getCanonicalType().getTypePtr())
103 continue;
104 // Check that the underlying expression is of the same class/struct type as the param i.e. that we are not instantiating
105 // something useful
106 if (t1.getCanonicalType().getTypePtr() != paramClassOrStructType)
107 continue;
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)))
113 continue;
116 if (m_Seen.insert(arg->getExprLoc()).second)
118 report(DiagnosticsEngine::Warning, "redundant functional cast from %0 to %1",
119 arg->getExprLoc())
120 << t2 << t1 << arg->getSourceRange();
121 report(DiagnosticsEngine::Note, "in call to method here", param->getLocation())
122 << param->getSourceRange();
125 return true;
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))
132 return true;
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>();
141 if (!lvalueType)
142 continue;
143 if (!lvalueType->getPointeeType().isConstQualified())
144 continue;
145 auto paramClassOrStructType = lvalueType->getPointeeType()->getAs<RecordType>();
146 if (!paramClassOrStructType)
147 continue;
148 // check for temporary and functional cast in argument expression
149 auto arg = callExpr->getArg(i)->IgnoreImplicit();
150 auto functionalCast = dyn_cast<CXXFunctionalCastExpr>(arg);
151 if (!functionalCast)
152 continue;
153 auto const t1 = functionalCast->getTypeAsWritten();
154 auto const t2 = compat::getSubExprAsWritten(functionalCast)->getType();
155 if (t1.getCanonicalType().getTypePtr() != t2.getCanonicalType().getTypePtr())
156 continue;
157 // Check that the underlying expression is of the same class/struct type as the param i.e. that we are not instantiating
158 // something useful
159 if (t1.getCanonicalType().getTypePtr() != paramClassOrStructType)
160 continue;
162 if (m_Seen.insert(arg->getExprLoc()).second)
164 report(DiagnosticsEngine::Warning, "redundant functional cast from %0 to %1",
165 arg->getExprLoc())
166 << t2 << t1 << arg->getSourceRange();
167 report(DiagnosticsEngine::Note, "in call to method here", param->getLocation())
168 << param->getSourceRange();
171 return true;
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;
179 QualType target;
180 auto const written = expr->getTypeAsWritten();
181 if (auto const t1 = written->getAs<DeducedTemplateSpecializationType>())
183 auto const decl = t1->getTemplateName().getAsTemplateDecl();
184 if (!decl)
186 return false;
188 if (!loplugin::DeclCheck(decl->getTemplatedDecl())
189 .ClassOrStruct("function")
190 .StdNamespace())
192 return false;
194 deduced = true;
196 else if (auto const t2 = written->getAs<TemplateSpecializationType>())
198 auto const decl = t2->getTemplateName().getAsTemplateDecl();
199 if (!decl)
201 return false;
203 if (!loplugin::DeclCheck(decl->getTemplatedDecl())
204 .ClassOrStruct("function")
205 .StdNamespace())
207 return false;
209 if (t2->getNumArgs() != 1)
211 if (isDebugMode())
213 report(DiagnosticsEngine::Fatal,
214 "TODO: unexpected std::function with %0 template arguments",
215 expr->getExprLoc())
216 << t2->getNumArgs() << expr->getSourceRange();
218 return false;
220 if (t2->getArg(0).getKind() != TemplateArgument::Type)
222 if (isDebugMode())
224 report(DiagnosticsEngine::Fatal,
225 "TODO: unexpected std::function with non-type template argument",
226 expr->getExprLoc())
227 << expr->getSourceRange();
229 return false;
231 target = t2->getArg(0).getAsType();
233 else
235 return false;
237 auto cxxConstruct = dyn_cast<CXXConstructExpr>(compat::IgnoreImplicit(expr->getSubExpr()));
238 if (!cxxConstruct)
239 return false;
240 auto const lambda = dyn_cast<LambdaExpr>(cxxConstruct->getArg(0));
241 if (!lambda)
242 return false;
243 if (deduced)
244 // std::function([...](Args)->Ret{...}) should always be redundant:
245 return true;
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());
252 auto const source
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))
262 return true;
263 // specifying the name for an init-list is necessary sometimes
264 if (isa<InitListExpr>(expr->getSubExpr()->IgnoreImplicit()))
265 return true;
266 if (isa<CXXStdInitializerListExpr>(expr->getSubExpr()->IgnoreImplicit()))
267 return true;
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)))
273 return true;
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))
283 return true;
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))
290 return true;
292 auto tc = loplugin::TypeCheck(t1);
293 if (tc.Typedef("sal_Int32").GlobalNamespace())
294 return true;
296 if (m_Seen.insert(expr->getExprLoc()).second)
297 report(DiagnosticsEngine::Warning, "redundant functional cast from %0 to %1",
298 expr->getExprLoc())
299 << t2 << t1 << expr->getSourceRange();
300 //getParentStmt(expr)->dump();
301 return true;
304 bool preRun() override
306 if (!compiler.getLangOpts().CPlusPlus)
307 return false;
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")
312 return false;
313 // compile-time check of constant
314 if (fn == SRCDIR "/bridges/source/jni_uno/jni_bridge.cxx")
315 return false;
316 return true;
319 void run() override
321 if (preRun())
322 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
325 std::unordered_set<SourceLocation> m_Seen;
328 static loplugin::Plugin::Registration<RedundantFCast> redundantfcast("redundantfcast");
330 } // namespace
332 #endif // LO_CLANG_SHARED_PLUGINS
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */