Remove exec bits from docx
[LibreOffice.git] / compilerplugins / clang / redundantcast.cxx
blob6bace528227515fa31022d8de13aa8e3d61d213e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 */
10 // Warn about certain redundant casts:
12 // * A reinterpret_cast<T*>(...) whose result is then implicitly cast to a void
13 // pointer
15 // * A static_cast<T*>(e) where e is of void pointer type and whose result is
16 // then implicitly cast to a void pointer
18 // * Various const_casts that are either not needed (like casting away constness
19 // in a delete expression) or are implicitly cast back afterwards
21 // C-style casts are ignored because it makes this plugin simpler, and they
22 // should eventually be eliminated via loplugin:cstylecast and/or
23 // -Wold-style-cast. That implies that this plugin is only relevant for C++
24 // code.
26 #include "clang/Sema/Sema.h"
28 #include "check.hxx"
29 #include "compat.hxx"
30 #include "plugin.hxx"
31 #include <iostream>
33 namespace {
35 bool isVoidPointer(QualType type) {
36 return type->isPointerType()
37 && type->getAs<clang::PointerType>()->getPointeeType()->isVoidType();
40 bool isRedundantConstCast(CXXConstCastExpr const * expr) {
41 auto const sub = compat::getSubExprAsWritten(expr);
42 return
43 (expr->getType().getCanonicalType()
44 == sub->getType().getCanonicalType())
45 && (expr->getValueKind() != VK_XValue
46 || sub->getValueKind() == VK_XValue);
49 bool canConstCastFromTo(Expr const * from, Expr const * to) {
50 auto const k1 = from->getValueKind();
51 auto const k2 = to->getValueKind();
52 return (k2 == VK_LValue && k1 == VK_LValue)
53 || (k2 == VK_XValue
54 && (k1 != compat::VK_PRValue || from->getType()->isRecordType()));
57 char const * printExprValueKind(ExprValueKind k) {
58 switch (k) {
59 case compat::VK_PRValue:
60 return "prvalue";
61 case VK_LValue:
62 return "lvalue";
63 case VK_XValue:
64 return "xvalue";
66 llvm_unreachable("unknown ExprValueKind");
69 QualType desugarElaboratedType(QualType type) {
70 if (auto const t = dyn_cast<ElaboratedType>(type)) {
71 return t->desugar();
73 return type;
76 enum class AlgebraicType { None, Integer, FloatingPoint };
78 AlgebraicType algebraicType(clang::Type const & type) {
79 if (type.isIntegralOrEnumerationType()) {
80 return AlgebraicType::Integer;
81 } else if (type.isRealFloatingType()) {
82 return AlgebraicType::FloatingPoint;
83 } else {
84 return AlgebraicType::None;
88 // Do not look through FunctionToPointerDecay, but through e.g. NullToPointer:
89 Expr const * stopAtFunctionPointerDecay(ExplicitCastExpr const * expr) {
90 auto const e1 = expr->getSubExpr();
91 if (auto const e2 = dyn_cast<ImplicitCastExpr>(e1)) {
92 if (e2->getCastKind() != CK_FunctionToPointerDecay) {
93 return e2->getSubExpr();
96 return e1;
99 class RedundantCast:
100 public loplugin::FilteringRewritePlugin<RedundantCast>
102 public:
103 explicit RedundantCast(loplugin::InstantiationData const & data):
104 FilteringRewritePlugin(data)
107 virtual void run() override {
108 if (compiler.getLangOpts().CPlusPlus) {
109 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
113 bool TraverseInitListExpr(InitListExpr * expr, DataRecursionQueue * queue = nullptr) {
114 return WalkUpFromInitListExpr(expr)
115 && TraverseSynOrSemInitListExpr(
116 expr->isSemanticForm() ? expr : expr->getSemanticForm(), queue);
119 bool TraverseReturnStmt(ReturnStmt * stmt) {
120 auto const saved = returnExpr_;
121 returnExpr_ = stmt->getRetValue();
122 auto const ret = FilteringRewritePlugin::TraverseReturnStmt(stmt);
123 returnExpr_ = saved;
124 return ret;
127 bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
129 bool VisitCXXStaticCastExpr(CXXStaticCastExpr const * expr);
131 bool VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr const * expr);
133 bool VisitCXXConstCastExpr(CXXConstCastExpr const * expr);
135 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr);
137 bool VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr);
139 bool VisitCallExpr(CallExpr const * expr);
141 bool VisitCXXDeleteExpr(CXXDeleteExpr const * expr);
143 bool VisitCStyleCastExpr(CStyleCastExpr const * expr);
145 bool VisitBinaryOperator(BinaryOperator const * expr) {
146 auto const op = expr->getOpcode();
147 if (op == BO_Sub || expr->isRelationalOp() || expr->isEqualityOp()) {
148 return visitBinOp(expr);
150 if (op == BO_Assign) {
151 if (ignoreLocation(expr)) {
152 return true;
154 visitAssign(expr->getLHS()->getType(), expr->getRHS());
155 return true;
157 return true;
160 bool VisitVarDecl(VarDecl const * varDecl);
162 private:
163 bool visitBinOp(BinaryOperator const * expr);
164 void visitAssign(QualType lhs, Expr const * rhs);
165 bool isOverloadedFunction(FunctionDecl const * decl);
167 bool isInIgnoredMacroBody(Expr const * expr) {
168 auto const loc = expr->getBeginLoc();
169 return compiler.getSourceManager().isMacroBodyExpansion(loc)
170 && ignoreLocation(compiler.getSourceManager().getSpellingLoc(loc));
173 Expr const * returnExpr_ = nullptr;
176 bool RedundantCast::VisitImplicitCastExpr(const ImplicitCastExpr * expr) {
177 if (ignoreLocation(expr)) {
178 return true;
180 switch (expr->getCastKind()) {
181 case CK_NoOp:
182 if (expr->getType()->isPointerType()
183 || expr->getType()->isObjectType())
185 auto e = dyn_cast<CXXConstCastExpr>(
186 expr->getSubExpr()->IgnoreParenImpCasts());
187 if (e != nullptr && !isRedundantConstCast(e)) {
188 auto t1 = e->getSubExpr()->getType().getCanonicalType();
189 auto t3 = expr->getType().getCanonicalType();
190 bool ObjCLifetimeConversion;
191 if (t1.getTypePtr() == t3.getTypePtr()
192 || (compiler.getSema().IsQualificationConversion(
193 t1, t3, false, ObjCLifetimeConversion)
194 && (e->getType().getCanonicalType().getTypePtr()
195 != t3.getTypePtr())))
197 report(
198 DiagnosticsEngine::Warning,
199 ("redundant const_cast from %0 to %1, result is"
200 " implicitly cast to %2"),
201 e->getExprLoc())
202 << e->getSubExprAsWritten()->getType() << e->getType()
203 << expr->getType() << expr->getSourceRange();
207 break;
208 case CK_BitCast:
209 if (isVoidPointer(expr->getType())
210 && expr->getSubExpr()->getType()->isPointerType())
212 Expr const * e = expr->getSubExpr()->IgnoreParenImpCasts();
213 while (isa<CXXConstCastExpr>(e)) {
214 auto cc = dyn_cast<CXXConstCastExpr>(e);
215 if (expr->getType()->getAs<clang::PointerType>()
216 ->getPointeeType().isAtLeastAsQualifiedAs(
217 cc->getSubExpr()->getType()
218 ->getAs<clang::PointerType>()->getPointeeType()))
220 report(
221 DiagnosticsEngine::Warning,
222 ("redundant const_cast from %0 to %1, result is"
223 " ultimately implicitly cast to %2"),
224 cc->getExprLoc())
225 << cc->getSubExprAsWritten()->getType() << cc->getType()
226 << expr->getType() << expr->getSourceRange();
228 e = cc->getSubExpr()->IgnoreParenImpCasts();
230 if (isa<CXXReinterpretCastExpr>(e)) {
231 report(
232 DiagnosticsEngine::Warning,
233 ("redundant reinterpret_cast, result is implicitly cast to"
234 " void pointer"),
235 e->getExprLoc())
236 << e->getSourceRange();
237 } else if (isa<CXXStaticCastExpr>(e)
238 && isVoidPointer(
239 dyn_cast<CXXStaticCastExpr>(e)->getSubExpr()
240 ->IgnoreParenImpCasts()->getType())
241 && !compiler.getSourceManager().isMacroBodyExpansion(
242 e->getBeginLoc()))
244 report(
245 DiagnosticsEngine::Warning,
246 ("redundant static_cast from void pointer, result is"
247 " implicitly cast to void pointer"),
248 e->getExprLoc())
249 << e->getSourceRange();
252 break;
253 case CK_DerivedToBase:
254 case CK_UncheckedDerivedToBase:
255 if (expr->getType()->isPointerType()) {
256 Expr const * e = expr->getSubExpr()->IgnoreParenImpCasts();
257 while (isa<CXXConstCastExpr>(e)) {
258 auto cc = dyn_cast<CXXConstCastExpr>(e);
259 if (expr->getType()->getAs<clang::PointerType>()
260 ->getPointeeType().isAtLeastAsQualifiedAs(
261 cc->getSubExpr()->getType()
262 ->getAs<clang::PointerType>()->getPointeeType()))
264 report(
265 DiagnosticsEngine::Warning,
266 ("redundant const_cast from %0 to %1, result is"
267 " ultimately implicitly cast to %2"),
268 cc->getExprLoc())
269 << cc->getSubExprAsWritten()->getType() << cc->getType()
270 << expr->getType() << expr->getSourceRange();
272 e = cc->getSubExpr()->IgnoreParenImpCasts();
274 } else if (expr->getType()->isReferenceType()) {
275 Expr const * e = expr->getSubExpr()->IgnoreParenImpCasts();
276 while (isa<CXXConstCastExpr>(e)) {
277 auto cc = dyn_cast<CXXConstCastExpr>(e);
278 if (expr->getType()->getAs<ReferenceType>()->getPointeeType()
279 .isAtLeastAsQualifiedAs(
280 cc->getSubExpr()->getType()
281 ->getAs<ReferenceType>()->getPointeeType()))
283 report(
284 DiagnosticsEngine::Warning,
285 ("redundant const_cast from %0 to %1, result is"
286 " ultimately implicitly cast to %2"),
287 cc->getExprLoc())
288 << cc->getSubExprAsWritten()->getType() << cc->getType()
289 << expr->getType() << expr->getSourceRange();
291 e = cc->getSubExpr()->IgnoreParenImpCasts();
294 break;
295 case CK_FloatingToIntegral:
296 case CK_IntegralToFloating:
297 if (auto e = dyn_cast<ExplicitCastExpr>(expr->getSubExpr()->IgnoreParenImpCasts())) {
298 if ((isa<CXXStaticCastExpr>(e) || isa<CXXFunctionalCastExpr>(e))
299 && (algebraicType(*e->getSubExprAsWritten()->getType())
300 == algebraicType(*expr->getType())))
302 report(
303 DiagnosticsEngine::Warning,
304 ("suspicious %select{static_cast|functional cast}0 from %1 to %2, result is"
305 " implicitly cast to %3"),
306 e->getExprLoc())
307 << isa<CXXFunctionalCastExpr>(e) << e->getSubExprAsWritten()->getType()
308 << e->getTypeAsWritten() << expr->getType() << expr->getSourceRange();
311 break;
312 default:
313 break;
315 return true;
318 bool RedundantCast::VisitCStyleCastExpr(CStyleCastExpr const * expr) {
319 if (ignoreLocation(expr)) {
320 return true;
322 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(expr->getBeginLoc()))) {
323 return true;
325 auto t1 = compat::getSubExprAsWritten(expr)->getType();
326 auto t2 = expr->getTypeAsWritten();
327 if (auto templateType = dyn_cast<SubstTemplateTypeParmType>(t1)) {
328 t1 = templateType->desugar();
330 if (desugarElaboratedType(t1) != desugarElaboratedType(t2)) {
331 return true;
333 if (!t1->isBuiltinType() && !loplugin::TypeCheck(t1).Enum() && !loplugin::TypeCheck(t1).Typedef()) {
334 return true;
336 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
338 return true;
340 // Ignore FD_ISSET expanding to "...(SOCKET)(fd)..." in some Microsoft
341 // winsock2.h (TODO: improve heuristic of determining that the whole
342 // expr is part of a single macro body expansion):
343 auto l1 = expr->getBeginLoc();
344 while (compiler.getSourceManager().isMacroArgExpansion(l1)) {
345 l1 = compiler.getSourceManager().getImmediateMacroCallerLoc(l1);
347 auto l2 = expr->getExprLoc();
348 while (compiler.getSourceManager().isMacroArgExpansion(l2)) {
349 l2 = compiler.getSourceManager().getImmediateMacroCallerLoc(l2);
351 auto l3 = expr->getEndLoc();
352 while (compiler.getSourceManager().isMacroArgExpansion(l3)) {
353 l3 = compiler.getSourceManager().getImmediateMacroCallerLoc(l3);
355 if (compiler.getSourceManager().isMacroBodyExpansion(l1)
356 && compiler.getSourceManager().isMacroBodyExpansion(l2)
357 && compiler.getSourceManager().isMacroBodyExpansion(l3)
358 && ignoreLocation(compiler.getSourceManager().getSpellingLoc(l2)))
360 return true;
362 report(
363 DiagnosticsEngine::Warning,
364 "redundant cstyle cast from %0 to %1", expr->getExprLoc())
365 << t1 << t2 << expr->getSourceRange();
366 return true;
369 bool RedundantCast::VisitVarDecl(VarDecl const * varDecl) {
370 if (ignoreLocation(varDecl)) {
371 return true;
373 if (!varDecl->getInit())
374 return true;
375 visitAssign(varDecl->getType(), varDecl->getInit());
376 if (varDecl->getInitStyle() != VarDecl::CInit
377 && isa<CXXTemporaryObjectExpr>(varDecl->getInit())
378 && !compiler.getSourceManager().isMacroBodyExpansion(varDecl->getInit()->getBeginLoc()))
380 report(
381 DiagnosticsEngine::Warning, "redundant functional cast",
382 varDecl->getBeginLoc())
383 << varDecl->getSourceRange();
385 return true;
388 void RedundantCast::visitAssign(QualType t1, Expr const * rhs)
390 auto staticCastExpr = dyn_cast<CXXStaticCastExpr>(rhs->IgnoreImplicit());
391 if (!staticCastExpr)
392 return;
394 auto const t2 = staticCastExpr->getSubExpr()->IgnoreImplicit()->getType();
396 // if there is more than one copy of the LHS, this cast is resolving ambiguity
397 bool foundOne = false;
398 if (t1->isRecordType())
400 foundOne = loplugin::derivedFromCount(t2, t1) == 1;
402 else
404 auto pointee1 = t1->getPointeeCXXRecordDecl();
405 auto pointee2 = t2->getPointeeCXXRecordDecl();
406 if (pointee1 && pointee2)
407 foundOne = loplugin::derivedFromCount(pointee2, pointee1) == 1;
410 if (foundOne)
412 report(
413 DiagnosticsEngine::Warning, "redundant static_cast from %0 to %1",
414 staticCastExpr->getExprLoc())
415 << t2 << t1 << staticCastExpr->getSourceRange();
419 bool RedundantCast::VisitCXXStaticCastExpr(CXXStaticCastExpr const * expr) {
420 if (ignoreLocation(expr)) {
421 return true;
423 auto const t2 = expr->getTypeAsWritten();
424 bool const fnptr = t2->isFunctionPointerType() || t2->isMemberFunctionPointerType();
425 auto const sub = fnptr ? stopAtFunctionPointerDecay(expr) : compat::getSubExprAsWritten(expr);
426 auto const t1 = sub->getType();
427 auto const nonClassObjectType = t2->isObjectType()
428 && !(t2->isRecordType() || t2->isArrayType());
429 if (nonClassObjectType && t2.hasLocalQualifiers()) {
430 report(
431 DiagnosticsEngine::Warning,
432 ("in static_cast from %0 %1 to %2 %3, remove redundant top-level"
433 " %select{const qualifier|volatile qualifier|const volatile"
434 " qualifiers}4"),
435 expr->getExprLoc())
436 << t1 << printExprValueKind(sub->getValueKind())
437 << t2 << printExprValueKind(expr->getValueKind())
438 << ((t2.isLocalConstQualified() ? 1 : 0)
439 + (t2.isLocalVolatileQualified() ? 2 : 0) - 1)
440 << expr->getSourceRange();
441 return true;
443 if (auto const impl = dyn_cast<ImplicitCastExpr>(expr->getSubExpr())) {
444 if (impl->getCastKind() == CK_ArrayToPointerDecay && impl->getType() == t2)
445 //TODO: instead of exact QualType match, allow some variation?
447 auto const fn = handler.getMainFileName();
448 if (!(loplugin::isSamePathname(
449 fn, SRCDIR "/sal/qa/rtl/strings/test_ostring_concat.cxx")
450 || loplugin::isSamePathname(
451 fn, SRCDIR "/sal/qa/rtl/strings/test_ostring_stringliterals.cxx")
452 || loplugin::isSamePathname(
453 fn, SRCDIR "/sal/qa/rtl/strings/test_oustring_concat.cxx")
454 || loplugin::isSamePathname(
455 fn, SRCDIR "/sal/qa/rtl/strings/test_oustring_stringliterals.cxx")
456 || isInIgnoredMacroBody(expr)))
458 report(
459 DiagnosticsEngine::Warning, "redundant static_cast from %0 to %1",
460 expr->getExprLoc())
461 << expr->getSubExprAsWritten()->getType() << t2 << expr->getSourceRange();
463 return true;
466 auto const t3 = expr->getType();
467 auto const c1 = t1.getCanonicalType();
468 auto const c3 = t3.getCanonicalType();
469 if (nonClassObjectType || !canConstCastFromTo(sub, expr)
470 ? c1.getTypePtr() != c3.getTypePtr() : c1 != c3)
472 bool ObjCLifetimeConversion;
473 if (nonClassObjectType
474 || (c1.getTypePtr() != c3.getTypePtr()
475 && !compiler.getSema().IsQualificationConversion(
476 c1, c3, false, ObjCLifetimeConversion)))
478 return true;
480 report(
481 DiagnosticsEngine::Warning,
482 "static_cast from %0 %1 to %2 %3 should be written as const_cast",
483 expr->getExprLoc())
484 << t1 << printExprValueKind(sub->getValueKind())
485 << t2 << printExprValueKind(expr->getValueKind())
486 << expr->getSourceRange();
487 return true;
489 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
491 return true;
493 // Don't warn if the types are 'void *' and at least one involves a typedef
494 // (and if both involve typedefs, they're different) (this covers cases like
495 // 'oslModule', or 'CURL *', or casts between 'LPVOID' and 'HANDLE' in
496 // Windows-only code):
497 if (loplugin::TypeCheck(t1).Pointer().NonConstVolatile().Void()) {
498 if (auto const td1 = t1->getAs<TypedefType>()) {
499 auto const td2 = t2->getAs<TypedefType>();
500 if (td2 == nullptr || td2 != td1) {
501 return true;
503 } else if (auto const td2 = t2->getAs<TypedefType>()) {
504 auto const td1 = t1->getAs<TypedefType>();
505 if (td1 == nullptr || td1 != td2) {
506 return true;
508 } else {
509 auto const pt1 = t1->getAs<clang::PointerType>()->getPointeeType();
510 auto const pt2 = t2->getAs<clang::PointerType>()->getPointeeType();
511 if (auto const ptd1 = pt1->getAs<TypedefType>()) {
512 auto const ptd2 = pt2->getAs<TypedefType>();
513 if (ptd2 == nullptr || ptd2 != ptd1) {
514 return true;
516 } else if (auto const ptd2 = pt2->getAs<TypedefType>()) {
517 auto const ptd1 = pt1->getAs<TypedefType>();
518 if (ptd1 == nullptr || ptd1 != ptd2) {
519 return true;
524 auto const k1 = sub->getValueKind();
525 auto const k3 = expr->getValueKind();
526 if ((k3 == VK_XValue && k1 != VK_XValue)
527 || (k3 == VK_LValue && k1 == VK_XValue))
529 return true;
531 // For <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2266r1.html> "P2266R1: Simpler
532 // implicit move" (as implemented with <https://github.com/llvm/llvm-project/commit/
533 // bf20631782183cd19e0bb7219e908c2bbb01a75f> "[clang] Implement P2266 Simpler implicit move"
534 // towards Clang 13), don't warn about a static_cast in a return statement like
536 // return static_cast<int &>(x);
538 // that needs an lvalue but where in a return statement like
540 // return x;
542 // the expression would now be an xvalue:
543 if (k3 == VK_LValue && k1 == VK_LValue && returnExpr_ != nullptr
544 && expr == returnExpr_->IgnoreParens())
546 return true;
548 // Don't warn if a static_cast on a pointer to function or member function is used to
549 // disambiguate an overloaded function:
550 if (fnptr) {
551 auto e = sub->IgnoreParenImpCasts();
552 if (auto const e1 = dyn_cast<UnaryOperator>(e)) {
553 if (e1->getOpcode() == UO_AddrOf) {
554 e = e1->getSubExpr()->IgnoreParenImpCasts();
557 if (auto const e1 = dyn_cast<DeclRefExpr>(e)) {
558 if (auto const fdecl = dyn_cast<FunctionDecl>(e1->getDecl())) {
559 if (isOverloadedFunction(fdecl)) {
560 return true;
565 // Suppress warnings from static_cast<bool> in C++ definition of assert in
566 // <https://sourceware.org/git/?p=glibc.git;a=commit;
567 // h=b5889d25e9bf944a89fdd7bcabf3b6c6f6bb6f7c> "assert: Support types
568 // without operator== (int) [BZ #21972]":
569 if (t1->isBooleanType() && t2->isBooleanType()) {
570 auto loc = expr->getBeginLoc();
571 if (compiler.getSourceManager().isMacroBodyExpansion(loc)
572 && (Lexer::getImmediateMacroName(
573 loc, compiler.getSourceManager(), compiler.getLangOpts())
574 == "assert"))
576 return true;
579 report(
580 DiagnosticsEngine::Warning,
581 ("static_cast from %0 %1 to %2 %3 is redundant%select{| or should be"
582 " written as an explicit construction of a temporary}4"),
583 expr->getExprLoc())
584 << t1 << printExprValueKind(k1) << t2 << printExprValueKind(k3)
585 << (k3 == compat::VK_PRValue && (k1 != compat::VK_PRValue || t1->isRecordType()))
586 << expr->getSourceRange();
587 return true;
590 bool RedundantCast::VisitCXXReinterpretCastExpr(
591 CXXReinterpretCastExpr const * expr)
593 if (ignoreLocation(expr)) {
594 return true;
596 if (expr->getTypeAsWritten() == expr->getSubExprAsWritten()->getType())
597 //TODO: instead of exact QualType match, allow some variation?
599 report(
600 DiagnosticsEngine::Warning, "redundant reinterpret_cast from %0 to %1",
601 expr->getExprLoc())
602 << expr->getSubExprAsWritten()->getType() << expr->getTypeAsWritten()
603 << expr->getSourceRange();
604 return true;
606 if (auto const sub = dyn_cast<ImplicitCastExpr>(expr->getSubExpr())) {
607 if (sub->getCastKind() == CK_ArrayToPointerDecay && sub->getType() == expr->getType())
608 //TODO: instead of exact QualType match, allow some variation?
610 if (loplugin::TypeCheck(sub->getType()).Pointer().Const().Char()) {
611 if (auto const lit = dyn_cast<clang::StringLiteral>(expr->getSubExprAsWritten())) {
612 if (lit->getKind() == compat::StringLiteralKind::UTF8) {
613 // Don't warn about
615 // redundant_cast<char const *>(u8"...")
617 // in pre-C++2a code:
618 return true;
622 report(
623 DiagnosticsEngine::Warning, "redundant reinterpret_cast from %0 to %1",
624 expr->getExprLoc())
625 << expr->getSubExprAsWritten()->getType() << expr->getTypeAsWritten()
626 << expr->getSourceRange();
627 return true;
630 if (expr->getSubExpr()->getType()->isVoidPointerType()) {
631 auto t = expr->getType()->getAs<clang::PointerType>();
632 if (t == nullptr || !t->getPointeeType()->isObjectType()) {
633 return true;
635 if (rewriter != nullptr) {
636 auto loc = expr->getBeginLoc();
637 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
638 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(
639 loc);
641 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
642 auto loc2 = expr->getEndLoc();
643 while (compiler.getSourceManager().isMacroArgExpansion(loc2)) {
644 loc2 = compiler.getSourceManager()
645 .getImmediateMacroCallerLoc(loc2);
647 if (compiler.getSourceManager().isMacroBodyExpansion(loc2)) {
648 //TODO: check loc, loc2 are in same macro body expansion
649 loc = compiler.getSourceManager().getSpellingLoc(loc);
652 auto s = compiler.getSourceManager().getCharacterData(loc);
653 auto n = Lexer::MeasureTokenLength(
654 loc, compiler.getSourceManager(), compiler.getLangOpts());
655 std::string tok(s, n);
656 if (tok == "reinterpret_cast" && replaceText(loc, n, "static_cast"))
658 return true;
661 report(
662 DiagnosticsEngine::Warning,
663 "reinterpret_cast from %0 to %1 can be simplified to static_cast",
664 expr->getExprLoc())
665 << expr->getSubExprAsWritten()->getType() << expr->getType()
666 << expr->getSourceRange();
667 } else if (expr->getType()->isVoidPointerType()) {
668 auto t = expr->getSubExpr()->getType()->getAs<clang::PointerType>();
669 if (t == nullptr || !t->getPointeeType()->isObjectType()) {
670 return true;
672 report(
673 DiagnosticsEngine::Warning,
674 ("reinterpret_cast from %0 to %1 can be simplified to static_cast"
675 " or an implicit conversion"),
676 expr->getExprLoc())
677 << expr->getSubExprAsWritten()->getType() << expr->getType()
678 << expr->getSourceRange();
679 } else if (expr->getType()->isFundamentalType()) {
680 if (auto const sub = dyn_cast<CXXConstCastExpr>(
681 expr->getSubExpr()->IgnoreParens()))
683 report(
684 DiagnosticsEngine::Warning,
685 ("redundant const_cast from %0 to %1 within reinterpret_cast to"
686 " fundamental type %2"),
687 expr->getExprLoc())
688 << sub->getSubExprAsWritten()->getType()
689 << sub->getTypeAsWritten() << expr->getTypeAsWritten()
690 << expr->getSourceRange();
691 return true;
694 if (auto const t1 = expr->getSubExpr()->getType()->getAs<clang::PointerType>()) {
695 if (auto const t2 = expr->getType()->getAs<clang::PointerType>()) {
696 if (auto const d1 = t1->getPointeeCXXRecordDecl()) {
697 if (auto const d2 = t2->getPointeeCXXRecordDecl()) {
698 if (d1->hasDefinition() && d1->isDerivedFrom(d2)) {
699 report(
700 DiagnosticsEngine::Warning,
701 "suspicious reinterpret_cast from derived %0 to base %1, maybe this was"
702 " meant to be a static_cast",
703 expr->getExprLoc())
704 << expr->getSubExprAsWritten()->getType() << expr->getTypeAsWritten()
705 << expr->getSourceRange();
706 return true;
712 return true;
715 bool RedundantCast::VisitCXXConstCastExpr(CXXConstCastExpr const * expr) {
716 if (ignoreLocation(expr)) {
717 return true;
719 auto const sub = compat::getSubExprAsWritten(expr);
720 if (isRedundantConstCast(expr)) {
721 report(
722 DiagnosticsEngine::Warning,
723 "redundant const_cast from %0 %1 to %2 %3", expr->getExprLoc())
724 << sub->getType() << printExprValueKind(sub->getValueKind())
725 << expr->getTypeAsWritten()
726 << printExprValueKind(expr->getValueKind())
727 << expr->getSourceRange();
728 return true;
730 if (auto const dce = dyn_cast<CXXStaticCastExpr>(
731 sub->IgnoreParenImpCasts()))
733 auto const sub2 = compat::getSubExprAsWritten(dce);
734 auto t1 = sub2->getType().getCanonicalType();
735 auto isNullptr = t1->isNullPtrType();
736 auto t2 = dce->getType().getCanonicalType();
737 auto t3 = expr->getType().getCanonicalType();
738 auto redundant = false;
739 for (;;) {
740 if ((t2.isConstQualified()
741 && (isNullptr || !t1.isConstQualified())
742 && !t3.isConstQualified())
743 || (t2.isVolatileQualified()
744 && (isNullptr || !t1.isVolatileQualified())
745 && !t3.isVolatileQualified()))
747 redundant = true;
748 break;
750 if (!isNullptr) {
751 auto const p1 = t1->getAs<clang::PointerType>();
752 if (p1 == nullptr) {
753 break;
755 t1 = p1->getPointeeType();
756 isNullptr = t1->isNullPtrType();
758 auto const p2 = t2->getAs<clang::PointerType>();
759 if (p2 == nullptr) {
760 break;
762 t2 = p2->getPointeeType();
763 auto const p3 = t3->getAs<clang::PointerType>();
764 if (p3 == nullptr) {
765 break;
767 t3 = p3->getPointeeType();
769 if (redundant) {
770 report(
771 DiagnosticsEngine::Warning,
772 ("redundant static_cast/const_cast combination from %0 via %1"
773 " to %2"),
774 expr->getExprLoc())
775 << sub2->getType() << dce->getTypeAsWritten()
776 << expr->getTypeAsWritten() << expr->getSourceRange();
779 return true;
782 bool RedundantCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr) {
783 if (ignoreLocation(expr)) {
784 return true;
786 // Restrict this to "real" casts (compared to uses of braced-init-list, like
788 // Foo{bar, baz}
790 // or
792 // std::initializer_list<Foo>{bar, baz}
794 // ), and only to cases where the sub-expression already is a prvalue of
795 // non-class type (and thus the cast is unlikely to be meant to create a
796 // temporary):
797 auto const t1 = expr->getTypeAsWritten();
798 bool const fnptr = t1->isFunctionPointerType() || t1->isMemberFunctionPointerType();
799 auto const sub = fnptr ? stopAtFunctionPointerDecay(expr) : compat::getSubExprAsWritten(expr);
800 if ((sub->getValueKind() != compat::VK_PRValue && !fnptr) || expr->getType()->isRecordType()
801 || isa<InitListExpr>(sub) || isa<CXXStdInitializerListExpr>(sub))
803 return true;
806 // See "There might even be good reasons(?) not to warn inside explicit
807 // casts" block in compilerplugins/clang/test/cppunitassertequals.cxx:
808 auto const eloc = expr->getExprLoc();
809 if (compiler.getSourceManager().isMacroArgExpansion(eloc)) {
810 auto const name = Lexer::getImmediateMacroName(
811 eloc, compiler.getSourceManager(), compiler.getLangOpts());
812 if (name == "CPPUNIT_ASSERT" || name == "CPPUNIT_ASSERT_MESSAGE") {
813 return true;
817 // Don't warn if a functional cast on a pointer to function or member function is used to
818 // disambiguate an overloaded function:
819 if (fnptr) {
820 auto e = sub->IgnoreParenImpCasts();
821 if (auto const e1 = dyn_cast<UnaryOperator>(e)) {
822 if (e1->getOpcode() == UO_AddrOf) {
823 e = e1->getSubExpr()->IgnoreParenImpCasts();
826 if (auto const e1 = dyn_cast<DeclRefExpr>(e)) {
827 if (auto const fdecl = dyn_cast<FunctionDecl>(e1->getDecl())) {
828 if (isOverloadedFunction(fdecl)) {
829 return true;
835 // See the commit message of d0e7d020fa405ab94f19916ec96fbd4611da0031
836 // "socket.c -> socket.cxx" for the reason to have
838 // bool(FD_ISSET(...))
840 // in sal/osl/unx/socket.cxx:
841 //TODO: Better check that sub is exactly an expansion of FD_ISSET:
842 if (sub->getEndLoc().isMacroID()) {
843 for (auto loc = sub->getBeginLoc();
844 loc.isMacroID()
845 && (compiler.getSourceManager()
846 .isAtStartOfImmediateMacroExpansion(loc));
847 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc))
849 if (Lexer::getImmediateMacroName(
850 loc, compiler.getSourceManager(), compiler.getLangOpts())
851 == "FD_ISSET")
853 return true;
858 auto const t2 = sub->getType();
859 if (t1.getCanonicalType() != t2.getCanonicalType())
860 return true;
861 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
862 return true;
863 report(
864 DiagnosticsEngine::Warning,
865 "redundant functional cast from %0 to %1", expr->getExprLoc())
866 << t2 << t1 << expr->getSourceRange();
867 return true;
870 bool RedundantCast::VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr) {
871 if (ignoreLocation(expr)) {
872 return true;
874 auto const sub = compat::getSubExprAsWritten(expr);
875 auto const t1 = expr->getTypeAsWritten();
876 auto const t2 = sub->getType();
877 QualType qt1 = t1.getCanonicalType();
878 QualType qt2 = t2.getCanonicalType();
879 if (qt1 == qt2)
881 report(
882 DiagnosticsEngine::Warning,
883 "redundant dynamic cast from %0 to %1", expr->getExprLoc())
884 << t2 << t1 << expr->getSourceRange();
885 return true;
887 if (qt1->isPointerType() && qt2->isPointerType())
889 // casting from 'T*' to 'const T*' is redundant, so compare without the qualifiers
890 qt1 = qt1->getPointeeType().getUnqualifiedType();
891 qt2 = qt2->getPointeeType().getUnqualifiedType();
892 if (qt1 == qt2)
894 report(
895 DiagnosticsEngine::Warning,
896 "redundant dynamic cast from %0 to %1", expr->getExprLoc())
897 << t2 << t1 << expr->getSourceRange();
898 return true;
900 if (qt1->getAsCXXRecordDecl() && qt2->getAsCXXRecordDecl()->isDerivedFrom(qt1->getAsCXXRecordDecl()))
902 report(
903 DiagnosticsEngine::Warning,
904 "redundant dynamic upcast from %0 to %1", expr->getExprLoc())
905 << t2 << t1 << expr->getSourceRange();
906 return true;
909 else if (qt1->isReferenceType() && qt2->isRecordType())
911 // casting from 'T&' to 'const T&' is redundant, so compare without the qualifiers
912 qt1 = qt1->getPointeeType().getUnqualifiedType();
913 qt2 = qt2.getUnqualifiedType();
914 if (qt1 == qt2)
916 report(
917 DiagnosticsEngine::Warning,
918 "redundant dynamic cast from %0 to %1", expr->getExprLoc())
919 << t2 << t1 << expr->getSourceRange();
920 return true;
922 if (qt1->getAsCXXRecordDecl() && qt2->getAsCXXRecordDecl()->isDerivedFrom(qt1->getAsCXXRecordDecl()))
924 report(
925 DiagnosticsEngine::Warning,
926 "redundant dynamic upcast from %0 to %1", expr->getExprLoc())
927 << t2 << t1 << expr->getSourceRange();
928 return true;
931 return true;
934 bool RedundantCast::VisitCallExpr(CallExpr const * expr) {
935 if (ignoreLocation(expr)) {
936 return true;
938 auto f = expr->getDirectCallee();
939 if (f == nullptr || !f->isVariadic()
940 || expr->getNumArgs() <= f->getNumParams())
942 return true;
944 for (auto i = f->getNumParams(); i != expr->getNumArgs(); ++i) {
945 auto a = expr->getArg(i);
946 if (a->getType()->isPointerType()) {
947 auto e = dyn_cast<CXXConstCastExpr>(a->IgnoreParenImpCasts());
948 if (e != nullptr) {
949 report(
950 DiagnosticsEngine::Warning,
951 "redundant const_cast of variadic function argument",
952 e->getExprLoc())
953 << expr->getSourceRange();
957 return true;
960 bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr const * expr) {
961 if (ignoreLocation(expr)) {
962 return true;
964 auto e = dyn_cast<CXXConstCastExpr>(
965 expr->getArgument()->IgnoreParenImpCasts());
966 if (e != nullptr) {
967 report(
968 DiagnosticsEngine::Warning,
969 "redundant const_cast in delete expression", e->getExprLoc())
970 << expr->getSourceRange();
972 return true;
975 bool RedundantCast::visitBinOp(BinaryOperator const * expr) {
976 if (ignoreLocation(expr)) {
977 return true;
979 if (expr->getLHS()->getType()->isPointerType()
980 && expr->getRHS()->getType()->isPointerType())
982 auto e = dyn_cast<CXXConstCastExpr>(
983 expr->getLHS()->IgnoreParenImpCasts());
984 if (e != nullptr) {
985 report(
986 DiagnosticsEngine::Warning,
987 "redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
988 e->getExprLoc())
989 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
991 e = dyn_cast<CXXConstCastExpr>(
992 expr->getRHS()->IgnoreParenImpCasts());
993 if (e != nullptr) {
994 report(
995 DiagnosticsEngine::Warning,
996 "redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
997 e->getExprLoc())
998 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
1001 return true;
1004 bool RedundantCast::isOverloadedFunction(FunctionDecl const * decl) {
1005 auto const ctx = decl->getDeclContext();
1006 if (!ctx->isLookupContext()) {
1007 return false;
1009 auto const canon = decl->getCanonicalDecl();
1010 auto const res = ctx->lookup(decl->getDeclName());
1011 for (auto d = res.begin(); d != res.end(); ++d) {
1012 if (auto const f = dyn_cast<FunctionDecl>(*d)) {
1013 if (f->getCanonicalDecl() != canon) {
1014 return true;
1018 return false;
1021 loplugin::Plugin::Registration<RedundantCast> X("redundantcast", true);
1025 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */