android: Reuse launcher icon in activities
[LibreOffice.git] / compilerplugins / clang / redundantcast.cxx
blob80485e0e9bd6c2d7538ab1298a4ac6afc157eee0
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 return true;
379 void RedundantCast::visitAssign(QualType t1, Expr const * rhs)
381 auto staticCastExpr = dyn_cast<CXXStaticCastExpr>(rhs->IgnoreImplicit());
382 if (!staticCastExpr)
383 return;
385 auto const t2 = staticCastExpr->getSubExpr()->IgnoreImplicit()->getType();
387 // if there is more than one copy of the LHS, this cast is resolving ambiguity
388 bool foundOne = false;
389 if (t1->isRecordType())
391 foundOne = loplugin::derivedFromCount(t2, t1) == 1;
393 else
395 auto pointee1 = t1->getPointeeCXXRecordDecl();
396 auto pointee2 = t2->getPointeeCXXRecordDecl();
397 if (pointee1 && pointee2)
398 foundOne = loplugin::derivedFromCount(pointee2, pointee1) == 1;
401 if (foundOne)
403 report(
404 DiagnosticsEngine::Warning, "redundant static_cast from %0 to %1",
405 staticCastExpr->getExprLoc())
406 << t2 << t1 << staticCastExpr->getSourceRange();
410 bool RedundantCast::VisitCXXStaticCastExpr(CXXStaticCastExpr const * expr) {
411 if (ignoreLocation(expr)) {
412 return true;
414 auto const t2 = expr->getTypeAsWritten();
415 bool const fnptr = t2->isFunctionPointerType() || t2->isMemberFunctionPointerType();
416 auto const sub = fnptr ? stopAtFunctionPointerDecay(expr) : compat::getSubExprAsWritten(expr);
417 auto const t1 = sub->getType();
418 auto const nonClassObjectType = t2->isObjectType()
419 && !(t2->isRecordType() || t2->isArrayType());
420 if (nonClassObjectType && t2.hasLocalQualifiers()) {
421 report(
422 DiagnosticsEngine::Warning,
423 ("in static_cast from %0 %1 to %2 %3, remove redundant top-level"
424 " %select{const qualifier|volatile qualifier|const volatile"
425 " qualifiers}4"),
426 expr->getExprLoc())
427 << t1 << printExprValueKind(sub->getValueKind())
428 << t2 << printExprValueKind(expr->getValueKind())
429 << ((t2.isLocalConstQualified() ? 1 : 0)
430 + (t2.isLocalVolatileQualified() ? 2 : 0) - 1)
431 << expr->getSourceRange();
432 return true;
434 if (auto const impl = dyn_cast<ImplicitCastExpr>(expr->getSubExpr())) {
435 if (impl->getCastKind() == CK_ArrayToPointerDecay && impl->getType() == t2)
436 //TODO: instead of exact QualType match, allow some variation?
438 auto const fn = handler.getMainFileName();
439 if (!(loplugin::isSamePathname(
440 fn, SRCDIR "/sal/qa/rtl/strings/test_ostring_concat.cxx")
441 || loplugin::isSamePathname(
442 fn, SRCDIR "/sal/qa/rtl/strings/test_ostring_stringliterals.cxx")
443 || loplugin::isSamePathname(
444 fn, SRCDIR "/sal/qa/rtl/strings/test_oustring_concat.cxx")
445 || loplugin::isSamePathname(
446 fn, SRCDIR "/sal/qa/rtl/strings/test_oustring_stringliterals.cxx")
447 || isInIgnoredMacroBody(expr)))
449 report(
450 DiagnosticsEngine::Warning, "redundant static_cast from %0 to %1",
451 expr->getExprLoc())
452 << expr->getSubExprAsWritten()->getType() << t2 << expr->getSourceRange();
454 return true;
457 auto const t3 = expr->getType();
458 auto const c1 = t1.getCanonicalType();
459 auto const c3 = t3.getCanonicalType();
460 if (nonClassObjectType || !canConstCastFromTo(sub, expr)
461 ? c1.getTypePtr() != c3.getTypePtr() : c1 != c3)
463 bool ObjCLifetimeConversion;
464 if (nonClassObjectType
465 || (c1.getTypePtr() != c3.getTypePtr()
466 && !compiler.getSema().IsQualificationConversion(
467 c1, c3, false, ObjCLifetimeConversion)))
469 return true;
471 report(
472 DiagnosticsEngine::Warning,
473 "static_cast from %0 %1 to %2 %3 should be written as const_cast",
474 expr->getExprLoc())
475 << t1 << printExprValueKind(sub->getValueKind())
476 << t2 << printExprValueKind(expr->getValueKind())
477 << expr->getSourceRange();
478 return true;
480 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
482 return true;
484 // Don't warn if the types are 'void *' and at least one involves a typedef
485 // (and if both involve typedefs, they're different) (this covers cases like
486 // 'oslModule', or 'CURL *', or casts between 'LPVOID' and 'HANDLE' in
487 // Windows-only code):
488 if (loplugin::TypeCheck(t1).Pointer().NonConstVolatile().Void()) {
489 if (auto const td1 = t1->getAs<TypedefType>()) {
490 auto const td2 = t2->getAs<TypedefType>();
491 if (td2 == nullptr || td2 != td1) {
492 return true;
494 } else if (auto const td2 = t2->getAs<TypedefType>()) {
495 auto const td1 = t1->getAs<TypedefType>();
496 if (td1 == nullptr || td1 != td2) {
497 return true;
499 } else {
500 auto const pt1 = t1->getAs<clang::PointerType>()->getPointeeType();
501 auto const pt2 = t2->getAs<clang::PointerType>()->getPointeeType();
502 if (auto const ptd1 = pt1->getAs<TypedefType>()) {
503 auto const ptd2 = pt2->getAs<TypedefType>();
504 if (ptd2 == nullptr || ptd2 != ptd1) {
505 return true;
507 } else if (auto const ptd2 = pt2->getAs<TypedefType>()) {
508 auto const ptd1 = pt1->getAs<TypedefType>();
509 if (ptd1 == nullptr || ptd1 != ptd2) {
510 return true;
515 auto const k1 = sub->getValueKind();
516 auto const k3 = expr->getValueKind();
517 if ((k3 == VK_XValue && k1 != VK_XValue)
518 || (k3 == VK_LValue && k1 == VK_XValue))
520 return true;
522 // For <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2266r1.html> "P2266R1: Simpler
523 // implicit move" (as implemented with <https://github.com/llvm/llvm-project/commit/
524 // bf20631782183cd19e0bb7219e908c2bbb01a75f> "[clang] Implement P2266 Simpler implicit move"
525 // towards Clang 13), don't warn about a static_cast in a return statement like
527 // return static_cast<int &>(x);
529 // that needs an lvalue but where in a return statement like
531 // return x;
533 // the expression would now be an xvalue:
534 if (k3 == VK_LValue && k1 == VK_LValue && returnExpr_ != nullptr
535 && expr == returnExpr_->IgnoreParens())
537 return true;
539 // Don't warn if a static_cast on a pointer to function or member function is used to
540 // disambiguate an overloaded function:
541 if (fnptr) {
542 auto e = sub->IgnoreParenImpCasts();
543 if (auto const e1 = dyn_cast<UnaryOperator>(e)) {
544 if (e1->getOpcode() == UO_AddrOf) {
545 e = e1->getSubExpr()->IgnoreParenImpCasts();
548 if (auto const e1 = dyn_cast<DeclRefExpr>(e)) {
549 if (auto const fdecl = dyn_cast<FunctionDecl>(e1->getDecl())) {
550 if (isOverloadedFunction(fdecl)) {
551 return true;
556 // Suppress warnings from static_cast<bool> in C++ definition of assert in
557 // <https://sourceware.org/git/?p=glibc.git;a=commit;
558 // h=b5889d25e9bf944a89fdd7bcabf3b6c6f6bb6f7c> "assert: Support types
559 // without operator== (int) [BZ #21972]":
560 if (t1->isBooleanType() && t2->isBooleanType()) {
561 auto loc = expr->getBeginLoc();
562 if (compiler.getSourceManager().isMacroBodyExpansion(loc)
563 && (Lexer::getImmediateMacroName(
564 loc, compiler.getSourceManager(), compiler.getLangOpts())
565 == "assert"))
567 return true;
570 report(
571 DiagnosticsEngine::Warning,
572 ("static_cast from %0 %1 to %2 %3 is redundant%select{| or should be"
573 " written as an explicit construction of a temporary}4"),
574 expr->getExprLoc())
575 << t1 << printExprValueKind(k1) << t2 << printExprValueKind(k3)
576 << (k3 == compat::VK_PRValue && (k1 != compat::VK_PRValue || t1->isRecordType()))
577 << expr->getSourceRange();
578 return true;
581 bool RedundantCast::VisitCXXReinterpretCastExpr(
582 CXXReinterpretCastExpr const * expr)
584 if (ignoreLocation(expr)) {
585 return true;
587 if (expr->getTypeAsWritten() == expr->getSubExprAsWritten()->getType())
588 //TODO: instead of exact QualType match, allow some variation?
590 report(
591 DiagnosticsEngine::Warning, "redundant reinterpret_cast from %0 to %1",
592 expr->getExprLoc())
593 << expr->getSubExprAsWritten()->getType() << expr->getTypeAsWritten()
594 << expr->getSourceRange();
595 return true;
597 if (auto const sub = dyn_cast<ImplicitCastExpr>(expr->getSubExpr())) {
598 if (sub->getCastKind() == CK_ArrayToPointerDecay && sub->getType() == expr->getType())
599 //TODO: instead of exact QualType match, allow some variation?
601 if (loplugin::TypeCheck(sub->getType()).Pointer().Const().Char()) {
602 if (auto const lit = dyn_cast<clang::StringLiteral>(expr->getSubExprAsWritten())) {
603 if (lit->getKind() == clang::StringLiteral::UTF8) {
604 // Don't warn about
606 // redundant_cast<char const *>(u8"...")
608 // in pre-C++2a code:
609 return true;
613 report(
614 DiagnosticsEngine::Warning, "redundant reinterpret_cast from %0 to %1",
615 expr->getExprLoc())
616 << expr->getSubExprAsWritten()->getType() << expr->getTypeAsWritten()
617 << expr->getSourceRange();
618 return true;
621 if (expr->getSubExpr()->getType()->isVoidPointerType()) {
622 auto t = expr->getType()->getAs<clang::PointerType>();
623 if (t == nullptr || !t->getPointeeType()->isObjectType()) {
624 return true;
626 if (rewriter != nullptr) {
627 auto loc = expr->getBeginLoc();
628 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
629 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(
630 loc);
632 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
633 auto loc2 = expr->getEndLoc();
634 while (compiler.getSourceManager().isMacroArgExpansion(loc2)) {
635 loc2 = compiler.getSourceManager()
636 .getImmediateMacroCallerLoc(loc2);
638 if (compiler.getSourceManager().isMacroBodyExpansion(loc2)) {
639 //TODO: check loc, loc2 are in same macro body expansion
640 loc = compiler.getSourceManager().getSpellingLoc(loc);
643 auto s = compiler.getSourceManager().getCharacterData(loc);
644 auto n = Lexer::MeasureTokenLength(
645 loc, compiler.getSourceManager(), compiler.getLangOpts());
646 std::string tok(s, n);
647 if (tok == "reinterpret_cast" && replaceText(loc, n, "static_cast"))
649 return true;
652 report(
653 DiagnosticsEngine::Warning,
654 "reinterpret_cast from %0 to %1 can be simplified to static_cast",
655 expr->getExprLoc())
656 << expr->getSubExprAsWritten()->getType() << expr->getType()
657 << expr->getSourceRange();
658 } else if (expr->getType()->isVoidPointerType()) {
659 auto t = expr->getSubExpr()->getType()->getAs<clang::PointerType>();
660 if (t == nullptr || !t->getPointeeType()->isObjectType()) {
661 return true;
663 report(
664 DiagnosticsEngine::Warning,
665 ("reinterpret_cast from %0 to %1 can be simplified to static_cast"
666 " or an implicit conversion"),
667 expr->getExprLoc())
668 << expr->getSubExprAsWritten()->getType() << expr->getType()
669 << expr->getSourceRange();
670 } else if (expr->getType()->isFundamentalType()) {
671 if (auto const sub = dyn_cast<CXXConstCastExpr>(
672 expr->getSubExpr()->IgnoreParens()))
674 report(
675 DiagnosticsEngine::Warning,
676 ("redundant const_cast from %0 to %1 within reinterpret_cast to"
677 " fundamental type %2"),
678 expr->getExprLoc())
679 << sub->getSubExprAsWritten()->getType()
680 << sub->getTypeAsWritten() << expr->getTypeAsWritten()
681 << expr->getSourceRange();
682 return true;
685 if (auto const t1 = expr->getSubExpr()->getType()->getAs<clang::PointerType>()) {
686 if (auto const t2 = expr->getType()->getAs<clang::PointerType>()) {
687 if (auto const d1 = t1->getPointeeCXXRecordDecl()) {
688 if (auto const d2 = t2->getPointeeCXXRecordDecl()) {
689 if (d1->hasDefinition() && d1->isDerivedFrom(d2)) {
690 report(
691 DiagnosticsEngine::Warning,
692 "suspicious reinterpret_cast from derived %0 to base %1, maybe this was"
693 " meant to be a static_cast",
694 expr->getExprLoc())
695 << expr->getSubExprAsWritten()->getType() << expr->getTypeAsWritten()
696 << expr->getSourceRange();
697 return true;
703 return true;
706 bool RedundantCast::VisitCXXConstCastExpr(CXXConstCastExpr const * expr) {
707 if (ignoreLocation(expr)) {
708 return true;
710 auto const sub = compat::getSubExprAsWritten(expr);
711 if (isRedundantConstCast(expr)) {
712 report(
713 DiagnosticsEngine::Warning,
714 "redundant const_cast from %0 %1 to %2 %3", expr->getExprLoc())
715 << sub->getType() << printExprValueKind(sub->getValueKind())
716 << expr->getTypeAsWritten()
717 << printExprValueKind(expr->getValueKind())
718 << expr->getSourceRange();
719 return true;
721 if (auto const dce = dyn_cast<CXXStaticCastExpr>(
722 sub->IgnoreParenImpCasts()))
724 auto const sub2 = compat::getSubExprAsWritten(dce);
725 auto t1 = sub2->getType().getCanonicalType();
726 auto isNullptr = t1->isNullPtrType();
727 auto t2 = dce->getType().getCanonicalType();
728 auto t3 = expr->getType().getCanonicalType();
729 auto redundant = false;
730 for (;;) {
731 if ((t2.isConstQualified()
732 && (isNullptr || !t1.isConstQualified())
733 && !t3.isConstQualified())
734 || (t2.isVolatileQualified()
735 && (isNullptr || !t1.isVolatileQualified())
736 && !t3.isVolatileQualified()))
738 redundant = true;
739 break;
741 if (!isNullptr) {
742 auto const p1 = t1->getAs<clang::PointerType>();
743 if (p1 == nullptr) {
744 break;
746 t1 = p1->getPointeeType();
747 isNullptr = t1->isNullPtrType();
749 auto const p2 = t2->getAs<clang::PointerType>();
750 if (p2 == nullptr) {
751 break;
753 t2 = p2->getPointeeType();
754 auto const p3 = t3->getAs<clang::PointerType>();
755 if (p3 == nullptr) {
756 break;
758 t3 = p3->getPointeeType();
760 if (redundant) {
761 report(
762 DiagnosticsEngine::Warning,
763 ("redundant static_cast/const_cast combination from %0 via %1"
764 " to %2"),
765 expr->getExprLoc())
766 << sub2->getType() << dce->getTypeAsWritten()
767 << expr->getTypeAsWritten() << expr->getSourceRange();
770 return true;
773 bool RedundantCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr) {
774 if (ignoreLocation(expr)) {
775 return true;
777 // Restrict this to "real" casts (compared to uses of braced-init-list, like
779 // Foo{bar, baz}
781 // or
783 // std::initializer_list<Foo>{bar, baz}
785 // ), and only to cases where the sub-expression already is a prvalue of
786 // non-class type (and thus the cast is unlikely to be meant to create a
787 // temporary):
788 auto const t1 = expr->getTypeAsWritten();
789 bool const fnptr = t1->isFunctionPointerType() || t1->isMemberFunctionPointerType();
790 auto const sub = fnptr ? stopAtFunctionPointerDecay(expr) : compat::getSubExprAsWritten(expr);
791 if ((sub->getValueKind() != compat::VK_PRValue && !fnptr) || expr->getType()->isRecordType()
792 || isa<InitListExpr>(sub) || isa<CXXStdInitializerListExpr>(sub))
794 return true;
797 // See "There might even be good reasons(?) not to warn inside explicit
798 // casts" block in compilerplugins/clang/test/cppunitassertequals.cxx:
799 auto const eloc = expr->getExprLoc();
800 if (compiler.getSourceManager().isMacroArgExpansion(eloc)) {
801 auto const name = Lexer::getImmediateMacroName(
802 eloc, compiler.getSourceManager(), compiler.getLangOpts());
803 if (name == "CPPUNIT_ASSERT" || name == "CPPUNIT_ASSERT_MESSAGE") {
804 return true;
808 // Don't warn if a functional cast on a pointer to function or member function is used to
809 // disambiguate an overloaded function:
810 if (fnptr) {
811 auto e = sub->IgnoreParenImpCasts();
812 if (auto const e1 = dyn_cast<UnaryOperator>(e)) {
813 if (e1->getOpcode() == UO_AddrOf) {
814 e = e1->getSubExpr()->IgnoreParenImpCasts();
817 if (auto const e1 = dyn_cast<DeclRefExpr>(e)) {
818 if (auto const fdecl = dyn_cast<FunctionDecl>(e1->getDecl())) {
819 if (isOverloadedFunction(fdecl)) {
820 return true;
826 // See the commit message of d0e7d020fa405ab94f19916ec96fbd4611da0031
827 // "socket.c -> socket.cxx" for the reason to have
829 // bool(FD_ISSET(...))
831 // in sal/osl/unx/socket.cxx:
832 //TODO: Better check that sub is exactly an expansion of FD_ISSET:
833 if (sub->getEndLoc().isMacroID()) {
834 for (auto loc = sub->getBeginLoc();
835 loc.isMacroID()
836 && (compiler.getSourceManager()
837 .isAtStartOfImmediateMacroExpansion(loc));
838 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc))
840 if (Lexer::getImmediateMacroName(
841 loc, compiler.getSourceManager(), compiler.getLangOpts())
842 == "FD_ISSET")
844 return true;
849 auto const t2 = sub->getType();
850 if (t1.getCanonicalType() != t2.getCanonicalType())
851 return true;
852 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
853 return true;
854 report(
855 DiagnosticsEngine::Warning,
856 "redundant functional cast from %0 to %1", expr->getExprLoc())
857 << t2 << t1 << expr->getSourceRange();
858 return true;
861 bool RedundantCast::VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr) {
862 if (ignoreLocation(expr)) {
863 return true;
865 auto const sub = compat::getSubExprAsWritten(expr);
866 auto const t1 = expr->getTypeAsWritten();
867 auto const t2 = sub->getType();
868 QualType qt1 = t1.getCanonicalType();
869 QualType qt2 = t2.getCanonicalType();
870 if (qt1 == qt2)
872 report(
873 DiagnosticsEngine::Warning,
874 "redundant dynamic cast from %0 to %1", expr->getExprLoc())
875 << t2 << t1 << expr->getSourceRange();
876 return true;
878 if (qt1->isPointerType() && qt2->isPointerType())
880 // casting from 'T*' to 'const T*' is redundant, so compare without the qualifiers
881 qt1 = qt1->getPointeeType().getUnqualifiedType();
882 qt2 = qt2->getPointeeType().getUnqualifiedType();
883 if (qt1 == qt2)
885 report(
886 DiagnosticsEngine::Warning,
887 "redundant dynamic cast from %0 to %1", expr->getExprLoc())
888 << t2 << t1 << expr->getSourceRange();
889 return true;
891 if (qt1->getAsCXXRecordDecl() && qt2->getAsCXXRecordDecl()->isDerivedFrom(qt1->getAsCXXRecordDecl()))
893 report(
894 DiagnosticsEngine::Warning,
895 "redundant dynamic upcast from %0 to %1", expr->getExprLoc())
896 << t2 << t1 << expr->getSourceRange();
897 return true;
900 else if (qt1->isReferenceType() && qt2->isRecordType())
902 // casting from 'T&' to 'const T&' is redundant, so compare without the qualifiers
903 qt1 = qt1->getPointeeType().getUnqualifiedType();
904 qt2 = qt2.getUnqualifiedType();
905 if (qt1 == qt2)
907 report(
908 DiagnosticsEngine::Warning,
909 "redundant dynamic cast from %0 to %1", expr->getExprLoc())
910 << t2 << t1 << expr->getSourceRange();
911 return true;
913 if (qt1->getAsCXXRecordDecl() && qt2->getAsCXXRecordDecl()->isDerivedFrom(qt1->getAsCXXRecordDecl()))
915 report(
916 DiagnosticsEngine::Warning,
917 "redundant dynamic upcast from %0 to %1", expr->getExprLoc())
918 << t2 << t1 << expr->getSourceRange();
919 return true;
922 return true;
925 bool RedundantCast::VisitCallExpr(CallExpr const * expr) {
926 if (ignoreLocation(expr)) {
927 return true;
929 auto f = expr->getDirectCallee();
930 if (f == nullptr || !f->isVariadic()
931 || expr->getNumArgs() <= f->getNumParams())
933 return true;
935 for (auto i = f->getNumParams(); i != expr->getNumArgs(); ++i) {
936 auto a = expr->getArg(i);
937 if (a->getType()->isPointerType()) {
938 auto e = dyn_cast<CXXConstCastExpr>(a->IgnoreParenImpCasts());
939 if (e != nullptr) {
940 report(
941 DiagnosticsEngine::Warning,
942 "redundant const_cast of variadic function argument",
943 e->getExprLoc())
944 << expr->getSourceRange();
948 return true;
951 bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr const * expr) {
952 if (ignoreLocation(expr)) {
953 return true;
955 auto e = dyn_cast<CXXConstCastExpr>(
956 expr->getArgument()->IgnoreParenImpCasts());
957 if (e != nullptr) {
958 report(
959 DiagnosticsEngine::Warning,
960 "redundant const_cast in delete expression", e->getExprLoc())
961 << expr->getSourceRange();
963 return true;
966 bool RedundantCast::visitBinOp(BinaryOperator const * expr) {
967 if (ignoreLocation(expr)) {
968 return true;
970 if (expr->getLHS()->getType()->isPointerType()
971 && expr->getRHS()->getType()->isPointerType())
973 auto e = dyn_cast<CXXConstCastExpr>(
974 expr->getLHS()->IgnoreParenImpCasts());
975 if (e != nullptr) {
976 report(
977 DiagnosticsEngine::Warning,
978 "redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
979 e->getExprLoc())
980 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
982 e = dyn_cast<CXXConstCastExpr>(
983 expr->getRHS()->IgnoreParenImpCasts());
984 if (e != nullptr) {
985 report(
986 DiagnosticsEngine::Warning,
987 "redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
988 e->getExprLoc())
989 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
992 return true;
995 bool RedundantCast::isOverloadedFunction(FunctionDecl const * decl) {
996 auto const ctx = decl->getDeclContext();
997 if (!ctx->isLookupContext()) {
998 return false;
1000 auto const canon = decl->getCanonicalDecl();
1001 auto const res = ctx->lookup(decl->getDeclName());
1002 for (auto d = res.begin(); d != res.end(); ++d) {
1003 if (auto const f = dyn_cast<FunctionDecl>(*d)) {
1004 if (f->getCanonicalDecl() != canon) {
1005 return true;
1009 return false;
1012 loplugin::Plugin::Registration<RedundantCast> X("redundantcast", true);
1016 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */