1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 // Warn about certain redundant casts:
12 // * A reinterpret_cast<T*>(...) whose result is then implicitly cast to a void
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++
26 #include "clang/Sema/Sema.h"
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
);
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
)
54 && (k1
!= compat::VK_PRValue
|| from
->getType()->isRecordType()));
57 char const * printExprValueKind(ExprValueKind k
) {
59 case compat::VK_PRValue
:
66 llvm_unreachable("unknown ExprValueKind");
69 QualType
desugarElaboratedType(QualType type
) {
70 if (auto const t
= dyn_cast
<ElaboratedType
>(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
;
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();
100 public loplugin::FilteringRewritePlugin
<RedundantCast
>
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
);
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
)) {
154 visitAssign(expr
->getLHS()->getType(), expr
->getRHS());
160 bool VisitVarDecl(VarDecl
const * varDecl
);
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
)) {
180 switch (expr
->getCastKind()) {
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())))
198 DiagnosticsEngine::Warning
,
199 ("redundant const_cast from %0 to %1, result is"
200 " implicitly cast to %2"),
202 << e
->getSubExprAsWritten()->getType() << e
->getType()
203 << expr
->getType() << expr
->getSourceRange();
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()))
221 DiagnosticsEngine::Warning
,
222 ("redundant const_cast from %0 to %1, result is"
223 " ultimately implicitly cast to %2"),
225 << cc
->getSubExprAsWritten()->getType() << cc
->getType()
226 << expr
->getType() << expr
->getSourceRange();
228 e
= cc
->getSubExpr()->IgnoreParenImpCasts();
230 if (isa
<CXXReinterpretCastExpr
>(e
)) {
232 DiagnosticsEngine::Warning
,
233 ("redundant reinterpret_cast, result is implicitly cast to"
236 << e
->getSourceRange();
237 } else if (isa
<CXXStaticCastExpr
>(e
)
239 dyn_cast
<CXXStaticCastExpr
>(e
)->getSubExpr()
240 ->IgnoreParenImpCasts()->getType())
241 && !compiler
.getSourceManager().isMacroBodyExpansion(
245 DiagnosticsEngine::Warning
,
246 ("redundant static_cast from void pointer, result is"
247 " implicitly cast to void pointer"),
249 << e
->getSourceRange();
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()))
265 DiagnosticsEngine::Warning
,
266 ("redundant const_cast from %0 to %1, result is"
267 " ultimately implicitly cast to %2"),
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()))
284 DiagnosticsEngine::Warning
,
285 ("redundant const_cast from %0 to %1, result is"
286 " ultimately implicitly cast to %2"),
288 << cc
->getSubExprAsWritten()->getType() << cc
->getType()
289 << expr
->getType() << expr
->getSourceRange();
291 e
= cc
->getSubExpr()->IgnoreParenImpCasts();
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())))
303 DiagnosticsEngine::Warning
,
304 ("suspicious %select{static_cast|functional cast}0 from %1 to %2, result is"
305 " implicitly cast to %3"),
307 << isa
<CXXFunctionalCastExpr
>(e
) << e
->getSubExprAsWritten()->getType()
308 << e
->getTypeAsWritten() << expr
->getType() << expr
->getSourceRange();
318 bool RedundantCast::VisitCStyleCastExpr(CStyleCastExpr
const * expr
) {
319 if (ignoreLocation(expr
)) {
322 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(expr
->getBeginLoc()))) {
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
)) {
333 if (!t1
->isBuiltinType() && !loplugin::TypeCheck(t1
).Enum() && !loplugin::TypeCheck(t1
).Typedef()) {
336 if (!loplugin::isOkToRemoveArithmeticCast(compiler
.getASTContext(), t1
, t2
, expr
->getSubExpr()))
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
)))
363 DiagnosticsEngine::Warning
,
364 "redundant cstyle cast from %0 to %1", expr
->getExprLoc())
365 << t1
<< t2
<< expr
->getSourceRange();
369 bool RedundantCast::VisitVarDecl(VarDecl
const * varDecl
) {
370 if (ignoreLocation(varDecl
)) {
373 if (!varDecl
->getInit())
375 visitAssign(varDecl
->getType(), varDecl
->getInit());
376 if (varDecl
->getInitStyle() != VarDecl::CInit
377 && isa
<CXXTemporaryObjectExpr
>(varDecl
->getInit())
378 && !compiler
.getSourceManager().isMacroBodyExpansion(varDecl
->getInit()->getBeginLoc()))
381 DiagnosticsEngine::Warning
, "redundant functional cast",
382 varDecl
->getBeginLoc())
383 << varDecl
->getSourceRange();
388 void RedundantCast::visitAssign(QualType t1
, Expr
const * rhs
)
390 auto staticCastExpr
= dyn_cast
<CXXStaticCastExpr
>(rhs
->IgnoreImplicit());
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;
404 auto pointee1
= t1
->getPointeeCXXRecordDecl();
405 auto pointee2
= t2
->getPointeeCXXRecordDecl();
406 if (pointee1
&& pointee2
)
407 foundOne
= loplugin::derivedFromCount(pointee2
, pointee1
) == 1;
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
)) {
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()) {
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"
436 << t1
<< printExprValueKind(sub
->getValueKind())
437 << t2
<< printExprValueKind(expr
->getValueKind())
438 << ((t2
.isLocalConstQualified() ? 1 : 0)
439 + (t2
.isLocalVolatileQualified() ? 2 : 0) - 1)
440 << expr
->getSourceRange();
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
)))
459 DiagnosticsEngine::Warning
, "redundant static_cast from %0 to %1",
461 << expr
->getSubExprAsWritten()->getType() << t2
<< expr
->getSourceRange();
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
)))
481 DiagnosticsEngine::Warning
,
482 "static_cast from %0 %1 to %2 %3 should be written as const_cast",
484 << t1
<< printExprValueKind(sub
->getValueKind())
485 << t2
<< printExprValueKind(expr
->getValueKind())
486 << expr
->getSourceRange();
489 if (!loplugin::isOkToRemoveArithmeticCast(compiler
.getASTContext(), t1
, t2
, expr
->getSubExpr()))
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
) {
503 } else if (auto const td2
= t2
->getAs
<TypedefType
>()) {
504 auto const td1
= t1
->getAs
<TypedefType
>();
505 if (td1
== nullptr || td1
!= td2
) {
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
) {
516 } else if (auto const ptd2
= pt2
->getAs
<TypedefType
>()) {
517 auto const ptd1
= pt1
->getAs
<TypedefType
>();
518 if (ptd1
== nullptr || ptd1
!= ptd2
) {
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
))
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
542 // the expression would now be an xvalue:
543 if (k3
== VK_LValue
&& k1
== VK_LValue
&& returnExpr_
!= nullptr
544 && expr
== returnExpr_
->IgnoreParens())
548 // Don't warn if a static_cast on a pointer to function or member function is used to
549 // disambiguate an overloaded function:
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
)) {
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())
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"),
584 << t1
<< printExprValueKind(k1
) << t2
<< printExprValueKind(k3
)
585 << (k3
== compat::VK_PRValue
&& (k1
!= compat::VK_PRValue
|| t1
->isRecordType()))
586 << expr
->getSourceRange();
590 bool RedundantCast::VisitCXXReinterpretCastExpr(
591 CXXReinterpretCastExpr
const * expr
)
593 if (ignoreLocation(expr
)) {
596 if (expr
->getTypeAsWritten() == expr
->getSubExprAsWritten()->getType())
597 //TODO: instead of exact QualType match, allow some variation?
600 DiagnosticsEngine::Warning
, "redundant reinterpret_cast from %0 to %1",
602 << expr
->getSubExprAsWritten()->getType() << expr
->getTypeAsWritten()
603 << expr
->getSourceRange();
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
) {
615 // redundant_cast<char const *>(u8"...")
617 // in pre-C++2a code:
623 DiagnosticsEngine::Warning
, "redundant reinterpret_cast from %0 to %1",
625 << expr
->getSubExprAsWritten()->getType() << expr
->getTypeAsWritten()
626 << expr
->getSourceRange();
630 if (expr
->getSubExpr()->getType()->isVoidPointerType()) {
631 auto t
= expr
->getType()->getAs
<clang::PointerType
>();
632 if (t
== nullptr || !t
->getPointeeType()->isObjectType()) {
635 if (rewriter
!= nullptr) {
636 auto loc
= expr
->getBeginLoc();
637 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
638 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(
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"))
662 DiagnosticsEngine::Warning
,
663 "reinterpret_cast from %0 to %1 can be simplified to static_cast",
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()) {
673 DiagnosticsEngine::Warning
,
674 ("reinterpret_cast from %0 to %1 can be simplified to static_cast"
675 " or an implicit conversion"),
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()))
684 DiagnosticsEngine::Warning
,
685 ("redundant const_cast from %0 to %1 within reinterpret_cast to"
686 " fundamental type %2"),
688 << sub
->getSubExprAsWritten()->getType()
689 << sub
->getTypeAsWritten() << expr
->getTypeAsWritten()
690 << expr
->getSourceRange();
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
)) {
700 DiagnosticsEngine::Warning
,
701 "suspicious reinterpret_cast from derived %0 to base %1, maybe this was"
702 " meant to be a static_cast",
704 << expr
->getSubExprAsWritten()->getType() << expr
->getTypeAsWritten()
705 << expr
->getSourceRange();
715 bool RedundantCast::VisitCXXConstCastExpr(CXXConstCastExpr
const * expr
) {
716 if (ignoreLocation(expr
)) {
719 auto const sub
= compat::getSubExprAsWritten(expr
);
720 if (isRedundantConstCast(expr
)) {
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();
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;
740 if ((t2
.isConstQualified()
741 && (isNullptr
|| !t1
.isConstQualified())
742 && !t3
.isConstQualified())
743 || (t2
.isVolatileQualified()
744 && (isNullptr
|| !t1
.isVolatileQualified())
745 && !t3
.isVolatileQualified()))
751 auto const p1
= t1
->getAs
<clang::PointerType
>();
755 t1
= p1
->getPointeeType();
756 isNullptr
= t1
->isNullPtrType();
758 auto const p2
= t2
->getAs
<clang::PointerType
>();
762 t2
= p2
->getPointeeType();
763 auto const p3
= t3
->getAs
<clang::PointerType
>();
767 t3
= p3
->getPointeeType();
771 DiagnosticsEngine::Warning
,
772 ("redundant static_cast/const_cast combination from %0 via %1"
775 << sub2
->getType() << dce
->getTypeAsWritten()
776 << expr
->getTypeAsWritten() << expr
->getSourceRange();
782 bool RedundantCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
const * expr
) {
783 if (ignoreLocation(expr
)) {
786 // Restrict this to "real" casts (compared to uses of braced-init-list, like
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
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
))
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") {
817 // Don't warn if a functional cast on a pointer to function or member function is used to
818 // disambiguate an overloaded function:
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
)) {
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();
845 && (compiler
.getSourceManager()
846 .isAtStartOfImmediateMacroExpansion(loc
));
847 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
))
849 if (Lexer::getImmediateMacroName(
850 loc
, compiler
.getSourceManager(), compiler
.getLangOpts())
858 auto const t2
= sub
->getType();
859 if (t1
.getCanonicalType() != t2
.getCanonicalType())
861 if (!loplugin::isOkToRemoveArithmeticCast(compiler
.getASTContext(), t1
, t2
, expr
->getSubExpr()))
864 DiagnosticsEngine::Warning
,
865 "redundant functional cast from %0 to %1", expr
->getExprLoc())
866 << t2
<< t1
<< expr
->getSourceRange();
870 bool RedundantCast::VisitCXXDynamicCastExpr(CXXDynamicCastExpr
const * expr
) {
871 if (ignoreLocation(expr
)) {
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();
882 DiagnosticsEngine::Warning
,
883 "redundant dynamic cast from %0 to %1", expr
->getExprLoc())
884 << t2
<< t1
<< expr
->getSourceRange();
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();
895 DiagnosticsEngine::Warning
,
896 "redundant dynamic cast from %0 to %1", expr
->getExprLoc())
897 << t2
<< t1
<< expr
->getSourceRange();
900 if (qt1
->getAsCXXRecordDecl() && qt2
->getAsCXXRecordDecl()->isDerivedFrom(qt1
->getAsCXXRecordDecl()))
903 DiagnosticsEngine::Warning
,
904 "redundant dynamic upcast from %0 to %1", expr
->getExprLoc())
905 << t2
<< t1
<< expr
->getSourceRange();
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();
917 DiagnosticsEngine::Warning
,
918 "redundant dynamic cast from %0 to %1", expr
->getExprLoc())
919 << t2
<< t1
<< expr
->getSourceRange();
922 if (qt1
->getAsCXXRecordDecl() && qt2
->getAsCXXRecordDecl()->isDerivedFrom(qt1
->getAsCXXRecordDecl()))
925 DiagnosticsEngine::Warning
,
926 "redundant dynamic upcast from %0 to %1", expr
->getExprLoc())
927 << t2
<< t1
<< expr
->getSourceRange();
934 bool RedundantCast::VisitCallExpr(CallExpr
const * expr
) {
935 if (ignoreLocation(expr
)) {
938 auto f
= expr
->getDirectCallee();
939 if (f
== nullptr || !f
->isVariadic()
940 || expr
->getNumArgs() <= f
->getNumParams())
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());
950 DiagnosticsEngine::Warning
,
951 "redundant const_cast of variadic function argument",
953 << expr
->getSourceRange();
960 bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr
const * expr
) {
961 if (ignoreLocation(expr
)) {
964 auto e
= dyn_cast
<CXXConstCastExpr
>(
965 expr
->getArgument()->IgnoreParenImpCasts());
968 DiagnosticsEngine::Warning
,
969 "redundant const_cast in delete expression", e
->getExprLoc())
970 << expr
->getSourceRange();
975 bool RedundantCast::visitBinOp(BinaryOperator
const * expr
) {
976 if (ignoreLocation(expr
)) {
979 if (expr
->getLHS()->getType()->isPointerType()
980 && expr
->getRHS()->getType()->isPointerType())
982 auto e
= dyn_cast
<CXXConstCastExpr
>(
983 expr
->getLHS()->IgnoreParenImpCasts());
986 DiagnosticsEngine::Warning
,
987 "redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
989 << (expr
->getOpcode() == BO_Sub
) << expr
->getSourceRange();
991 e
= dyn_cast
<CXXConstCastExpr
>(
992 expr
->getRHS()->IgnoreParenImpCasts());
995 DiagnosticsEngine::Warning
,
996 "redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
998 << (expr
->getOpcode() == BO_Sub
) << expr
->getSourceRange();
1004 bool RedundantCast::isOverloadedFunction(FunctionDecl
const * decl
) {
1005 auto const ctx
= decl
->getDeclContext();
1006 if (!ctx
->isLookupContext()) {
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
) {
1021 loplugin::Plugin::Registration
<RedundantCast
> X("redundantcast", true);
1025 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */