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
!= VK_RValue
|| from
->getType()->isRecordType()));
57 char const * printExprValueKind(ExprValueKind k
) {
66 llvm_unreachable("unknown ExprValueKind");
69 enum class AlgebraicType
{ None
, Integer
, FloatingPoint
};
71 AlgebraicType
algebraicType(clang::Type
const & type
) {
72 if (type
.isIntegralOrEnumerationType()) {
73 return AlgebraicType::Integer
;
74 } else if (type
.isRealFloatingType()) {
75 return AlgebraicType::FloatingPoint
;
77 return AlgebraicType::None
;
81 // Do not look through FunctionToPointerDecay, but through e.g. NullToPointer:
82 Expr
const * stopAtFunctionPointerDecay(ExplicitCastExpr
const * expr
) {
83 auto const e1
= expr
->getSubExpr();
84 if (auto const e2
= dyn_cast
<ImplicitCastExpr
>(e1
)) {
85 if (e2
->getCastKind() != CK_FunctionToPointerDecay
) {
86 return e2
->getSubExpr();
93 public loplugin::FilteringRewritePlugin
<RedundantCast
>
96 explicit RedundantCast(loplugin::InstantiationData
const & data
):
97 FilteringRewritePlugin(data
)
100 virtual void run() override
{
101 if (compiler
.getLangOpts().CPlusPlus
) {
102 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
106 bool TraverseInitListExpr(InitListExpr
* expr
, DataRecursionQueue
* queue
= nullptr) {
107 return WalkUpFromInitListExpr(expr
)
108 && TraverseSynOrSemInitListExpr(
109 expr
->isSemanticForm() ? expr
: expr
->getSemanticForm(), queue
);
112 bool VisitImplicitCastExpr(ImplicitCastExpr
const * expr
);
114 bool VisitCXXStaticCastExpr(CXXStaticCastExpr
const * expr
);
116 bool VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr
const * expr
);
118 bool VisitCXXConstCastExpr(CXXConstCastExpr
const * expr
);
120 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
const * expr
);
122 bool VisitCXXDynamicCastExpr(CXXDynamicCastExpr
const * expr
);
124 bool VisitCallExpr(CallExpr
const * expr
);
126 bool VisitCXXDeleteExpr(CXXDeleteExpr
const * expr
);
128 bool VisitCStyleCastExpr(CStyleCastExpr
const * expr
);
130 bool VisitBinaryOperator(BinaryOperator
const * expr
) {
131 auto const op
= expr
->getOpcode();
132 if (op
== BO_Sub
|| expr
->isRelationalOp() || expr
->isEqualityOp()) {
133 return visitBinOp(expr
);
135 if (op
== BO_Assign
) {
136 if (ignoreLocation(expr
)) {
139 visitAssign(expr
->getLHS()->getType(), expr
->getRHS());
145 bool VisitVarDecl(VarDecl
const * varDecl
);
148 bool visitBinOp(BinaryOperator
const * expr
);
149 void visitAssign(QualType lhs
, Expr
const * rhs
);
150 bool isOverloadedFunction(FunctionDecl
const * decl
);
152 bool isInIgnoredMacroBody(Expr
const * expr
) {
153 auto const loc
= compat::getBeginLoc(expr
);
154 return compiler
.getSourceManager().isMacroBodyExpansion(loc
)
155 && ignoreLocation(compiler
.getSourceManager().getSpellingLoc(loc
));
159 bool RedundantCast::VisitImplicitCastExpr(const ImplicitCastExpr
* expr
) {
160 if (ignoreLocation(expr
)) {
163 switch (expr
->getCastKind()) {
165 if (expr
->getType()->isPointerType()
166 || expr
->getType()->isObjectType())
168 auto e
= dyn_cast
<CXXConstCastExpr
>(
169 expr
->getSubExpr()->IgnoreParenImpCasts());
170 if (e
!= nullptr && !isRedundantConstCast(e
)) {
171 auto t1
= e
->getSubExpr()->getType().getCanonicalType();
172 auto t3
= expr
->getType().getCanonicalType();
173 bool ObjCLifetimeConversion
;
174 if (t1
.getTypePtr() == t3
.getTypePtr()
175 || (compiler
.getSema().IsQualificationConversion(
176 t1
, t3
, false, ObjCLifetimeConversion
)
177 && (e
->getType().getCanonicalType().getTypePtr()
178 != t3
.getTypePtr())))
181 DiagnosticsEngine::Warning
,
182 ("redundant const_cast from %0 to %1, result is"
183 " implicitly cast to %2"),
185 << e
->getSubExprAsWritten()->getType() << e
->getType()
186 << expr
->getType() << expr
->getSourceRange();
192 if (isVoidPointer(expr
->getType())
193 && expr
->getSubExpr()->getType()->isPointerType())
195 Expr
const * e
= expr
->getSubExpr()->IgnoreParenImpCasts();
196 while (isa
<CXXConstCastExpr
>(e
)) {
197 auto cc
= dyn_cast
<CXXConstCastExpr
>(e
);
198 if (expr
->getType()->getAs
<clang::PointerType
>()
199 ->getPointeeType().isAtLeastAsQualifiedAs(
200 cc
->getSubExpr()->getType()
201 ->getAs
<clang::PointerType
>()->getPointeeType()))
204 DiagnosticsEngine::Warning
,
205 ("redundant const_cast from %0 to %1, result is"
206 " ultimately implicitly cast to %2"),
208 << cc
->getSubExprAsWritten()->getType() << cc
->getType()
209 << expr
->getType() << expr
->getSourceRange();
211 e
= cc
->getSubExpr()->IgnoreParenImpCasts();
213 if (isa
<CXXReinterpretCastExpr
>(e
)) {
215 DiagnosticsEngine::Warning
,
216 ("redundant reinterpret_cast, result is implicitly cast to"
219 << e
->getSourceRange();
220 } else if (isa
<CXXStaticCastExpr
>(e
)
222 dyn_cast
<CXXStaticCastExpr
>(e
)->getSubExpr()
223 ->IgnoreParenImpCasts()->getType())
224 && !compiler
.getSourceManager().isMacroBodyExpansion(
225 compat::getBeginLoc(e
)))
228 DiagnosticsEngine::Warning
,
229 ("redundant static_cast from void pointer, result is"
230 " implicitly cast to void pointer"),
232 << e
->getSourceRange();
236 case CK_DerivedToBase
:
237 case CK_UncheckedDerivedToBase
:
238 if (expr
->getType()->isPointerType()) {
239 Expr
const * e
= expr
->getSubExpr()->IgnoreParenImpCasts();
240 while (isa
<CXXConstCastExpr
>(e
)) {
241 auto cc
= dyn_cast
<CXXConstCastExpr
>(e
);
242 if (expr
->getType()->getAs
<clang::PointerType
>()
243 ->getPointeeType().isAtLeastAsQualifiedAs(
244 cc
->getSubExpr()->getType()
245 ->getAs
<clang::PointerType
>()->getPointeeType()))
248 DiagnosticsEngine::Warning
,
249 ("redundant const_cast from %0 to %1, result is"
250 " ultimately implicitly cast to %2"),
252 << cc
->getSubExprAsWritten()->getType() << cc
->getType()
253 << expr
->getType() << expr
->getSourceRange();
255 e
= cc
->getSubExpr()->IgnoreParenImpCasts();
257 } else if (expr
->getType()->isReferenceType()) {
258 Expr
const * e
= expr
->getSubExpr()->IgnoreParenImpCasts();
259 while (isa
<CXXConstCastExpr
>(e
)) {
260 auto cc
= dyn_cast
<CXXConstCastExpr
>(e
);
261 if (expr
->getType()->getAs
<ReferenceType
>()->getPointeeType()
262 .isAtLeastAsQualifiedAs(
263 cc
->getSubExpr()->getType()
264 ->getAs
<ReferenceType
>()->getPointeeType()))
267 DiagnosticsEngine::Warning
,
268 ("redundant const_cast from %0 to %1, result is"
269 " ultimately implicitly cast to %2"),
271 << cc
->getSubExprAsWritten()->getType() << cc
->getType()
272 << expr
->getType() << expr
->getSourceRange();
274 e
= cc
->getSubExpr()->IgnoreParenImpCasts();
278 case CK_FloatingToIntegral
:
279 case CK_IntegralToFloating
:
280 if (auto e
= dyn_cast
<ExplicitCastExpr
>(expr
->getSubExpr()->IgnoreParenImpCasts())) {
281 if ((isa
<CXXStaticCastExpr
>(e
) || isa
<CXXFunctionalCastExpr
>(e
))
282 && (algebraicType(*e
->getSubExprAsWritten()->getType())
283 == algebraicType(*expr
->getType())))
286 DiagnosticsEngine::Warning
,
287 ("suspicious %select{static_cast|functional cast}0 from %1 to %2, result is"
288 " implicitly cast to %3"),
290 << isa
<CXXFunctionalCastExpr
>(e
) << e
->getSubExprAsWritten()->getType()
291 << e
->getTypeAsWritten() << expr
->getType() << expr
->getSourceRange();
301 bool RedundantCast::VisitCStyleCastExpr(CStyleCastExpr
const * expr
) {
302 if (ignoreLocation(expr
)) {
305 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr
)))) {
308 auto t1
= compat::getSubExprAsWritten(expr
)->getType();
309 auto t2
= expr
->getTypeAsWritten();
310 if (auto templateType
= dyn_cast
<SubstTemplateTypeParmType
>(t1
)) {
311 t1
= templateType
->desugar();
316 if (!t1
->isBuiltinType() && !loplugin::TypeCheck(t1
).Enum() && !loplugin::TypeCheck(t1
).Typedef()) {
319 if (!loplugin::isOkToRemoveArithmeticCast(compiler
.getASTContext(), t1
, t2
, expr
->getSubExpr()))
323 // Ignore FD_ISSET expanding to "...(SOCKET)(fd)..." in some Microsoft
324 // winsock2.h (TODO: improve heuristic of determining that the whole
325 // expr is part of a single macro body expansion):
326 auto l1
= compat::getBeginLoc(expr
);
327 while (compiler
.getSourceManager().isMacroArgExpansion(l1
)) {
328 l1
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l1
);
330 auto l2
= expr
->getExprLoc();
331 while (compiler
.getSourceManager().isMacroArgExpansion(l2
)) {
332 l2
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l2
);
334 auto l3
= compat::getEndLoc(expr
);
335 while (compiler
.getSourceManager().isMacroArgExpansion(l3
)) {
336 l3
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l3
);
338 if (compiler
.getSourceManager().isMacroBodyExpansion(l1
)
339 && compiler
.getSourceManager().isMacroBodyExpansion(l2
)
340 && compiler
.getSourceManager().isMacroBodyExpansion(l3
)
341 && ignoreLocation(compiler
.getSourceManager().getSpellingLoc(l2
)))
346 DiagnosticsEngine::Warning
,
347 "redundant cstyle cast from %0 to %1", expr
->getExprLoc())
348 << t1
<< t2
<< expr
->getSourceRange();
352 bool RedundantCast::VisitVarDecl(VarDecl
const * varDecl
) {
353 if (ignoreLocation(varDecl
)) {
356 if (!varDecl
->getInit())
358 visitAssign(varDecl
->getType(), varDecl
->getInit());
362 void RedundantCast::visitAssign(QualType t1
, Expr
const * rhs
)
364 auto staticCastExpr
= dyn_cast
<CXXStaticCastExpr
>(rhs
->IgnoreImplicit());
368 auto const t2
= staticCastExpr
->getSubExpr()->IgnoreImplicit()->getType();
370 // if there is more than one copy of the LHS, this cast is resolving ambiguity
371 bool foundOne
= false;
372 if (t1
->isRecordType())
374 foundOne
= loplugin::derivedFromCount(t2
, t1
) == 1;
378 auto pointee1
= t1
->getPointeeCXXRecordDecl();
379 auto pointee2
= t2
->getPointeeCXXRecordDecl();
380 if (pointee1
&& pointee2
)
381 foundOne
= loplugin::derivedFromCount(pointee2
, pointee1
) == 1;
387 DiagnosticsEngine::Warning
, "redundant static_cast from %0 to %1",
388 staticCastExpr
->getExprLoc())
389 << t2
<< t1
<< staticCastExpr
->getSourceRange();
393 bool RedundantCast::VisitCXXStaticCastExpr(CXXStaticCastExpr
const * expr
) {
394 if (ignoreLocation(expr
)) {
397 auto const t2
= expr
->getTypeAsWritten();
398 bool const fnptr
= t2
->isFunctionPointerType() || t2
->isMemberFunctionPointerType();
399 auto const sub
= fnptr
? stopAtFunctionPointerDecay(expr
) : compat::getSubExprAsWritten(expr
);
400 auto const t1
= sub
->getType();
401 auto const nonClassObjectType
= t2
->isObjectType()
402 && !(t2
->isRecordType() || t2
->isArrayType());
403 if (nonClassObjectType
&& t2
.hasLocalQualifiers()) {
405 DiagnosticsEngine::Warning
,
406 ("in static_cast from %0 %1 to %2 %3, remove redundant top-level"
407 " %select{const qualifier|volatile qualifier|const volatile"
410 << t1
<< printExprValueKind(sub
->getValueKind())
411 << t2
<< printExprValueKind(expr
->getValueKind())
412 << ((t2
.isLocalConstQualified() ? 1 : 0)
413 + (t2
.isLocalVolatileQualified() ? 2 : 0) - 1)
414 << expr
->getSourceRange();
417 if (auto const impl
= dyn_cast
<ImplicitCastExpr
>(expr
->getSubExpr())) {
418 if (impl
->getCastKind() == CK_ArrayToPointerDecay
&& impl
->getType() == t2
)
419 //TODO: instead of exact QualType match, allow some variation?
421 auto const fn
= handler
.getMainFileName();
422 if (!(loplugin::isSamePathname(
423 fn
, SRCDIR
"/sal/qa/rtl/strings/test_ostring_concat.cxx")
424 || loplugin::isSamePathname(
425 fn
, SRCDIR
"/sal/qa/rtl/strings/test_ostring_stringliterals.cxx")
426 || loplugin::isSamePathname(
427 fn
, SRCDIR
"/sal/qa/rtl/strings/test_oustring_concat.cxx")
428 || loplugin::isSamePathname(
429 fn
, SRCDIR
"/sal/qa/rtl/strings/test_oustring_stringliterals.cxx")
430 || isInIgnoredMacroBody(expr
)))
433 DiagnosticsEngine::Warning
, "redundant static_cast from %0 to %1",
435 << expr
->getSubExprAsWritten()->getType() << t2
<< expr
->getSourceRange();
440 auto const t3
= expr
->getType();
441 auto const c1
= t1
.getCanonicalType();
442 auto const c3
= t3
.getCanonicalType();
443 if (nonClassObjectType
|| !canConstCastFromTo(sub
, expr
)
444 ? c1
.getTypePtr() != c3
.getTypePtr() : c1
!= c3
)
446 bool ObjCLifetimeConversion
;
447 if (nonClassObjectType
448 || (c1
.getTypePtr() != c3
.getTypePtr()
449 && !compiler
.getSema().IsQualificationConversion(
450 c1
, c3
, false, ObjCLifetimeConversion
)))
455 DiagnosticsEngine::Warning
,
456 "static_cast from %0 %1 to %2 %3 should be written as const_cast",
458 << t1
<< printExprValueKind(sub
->getValueKind())
459 << t2
<< printExprValueKind(expr
->getValueKind())
460 << expr
->getSourceRange();
463 if (!loplugin::isOkToRemoveArithmeticCast(compiler
.getASTContext(), t1
, t2
, expr
->getSubExpr()))
467 // Don't warn if the types are 'void *' and at least one involves a typedef
468 // (and if both involve typedefs, they're different) (this covers cases like
469 // 'oslModule', or 'CURL *', or casts between 'LPVOID' and 'HANDLE' in
470 // Windows-only code):
471 if (loplugin::TypeCheck(t1
).Pointer().NonConstVolatile().Void()) {
472 if (auto const td1
= t1
->getAs
<TypedefType
>()) {
473 auto const td2
= t2
->getAs
<TypedefType
>();
474 if (td2
== nullptr || td2
!= td1
) {
477 } else if (auto const td2
= t2
->getAs
<TypedefType
>()) {
478 auto const td1
= t1
->getAs
<TypedefType
>();
479 if (td1
== nullptr || td1
!= td2
) {
483 auto const pt1
= t1
->getAs
<clang::PointerType
>()->getPointeeType();
484 auto const pt2
= t2
->getAs
<clang::PointerType
>()->getPointeeType();
485 if (auto const ptd1
= pt1
->getAs
<TypedefType
>()) {
486 auto const ptd2
= pt2
->getAs
<TypedefType
>();
487 if (ptd2
== nullptr || ptd2
!= ptd1
) {
490 } else if (auto const ptd2
= pt2
->getAs
<TypedefType
>()) {
491 auto const ptd1
= pt1
->getAs
<TypedefType
>();
492 if (ptd1
== nullptr || ptd1
!= ptd2
) {
498 auto const k1
= sub
->getValueKind();
499 auto const k3
= expr
->getValueKind();
500 if ((k3
== VK_XValue
&& k1
!= VK_XValue
)
501 || (k3
== VK_LValue
&& k1
== VK_XValue
))
505 // Don't warn if a static_cast on a pointer to function or member function is used to
506 // disambiguate an overloaded function:
508 auto e
= sub
->IgnoreParenImpCasts();
509 if (auto const e1
= dyn_cast
<UnaryOperator
>(e
)) {
510 if (e1
->getOpcode() == UO_AddrOf
) {
511 e
= e1
->getSubExpr()->IgnoreParenImpCasts();
514 if (auto const e1
= dyn_cast
<DeclRefExpr
>(e
)) {
515 if (auto const fdecl
= dyn_cast
<FunctionDecl
>(e1
->getDecl())) {
516 if (isOverloadedFunction(fdecl
)) {
522 // Suppress warnings from static_cast<bool> in C++ definition of assert in
523 // <https://sourceware.org/git/?p=glibc.git;a=commit;
524 // h=b5889d25e9bf944a89fdd7bcabf3b6c6f6bb6f7c> "assert: Support types
525 // without operator== (int) [BZ #21972]":
526 if (t1
->isBooleanType() && t2
->isBooleanType()) {
527 auto loc
= compat::getBeginLoc(expr
);
528 if (compiler
.getSourceManager().isMacroBodyExpansion(loc
)
529 && (Lexer::getImmediateMacroName(
530 loc
, compiler
.getSourceManager(), compiler
.getLangOpts())
537 DiagnosticsEngine::Warning
,
538 ("static_cast from %0 %1 to %2 %3 is redundant%select{| or should be"
539 " written as an explicit construction of a temporary}4"),
541 << t1
<< printExprValueKind(k1
) << t2
<< printExprValueKind(k3
)
542 << (k3
== VK_RValue
&& (k1
!= VK_RValue
|| t1
->isRecordType()))
543 << expr
->getSourceRange();
547 bool RedundantCast::VisitCXXReinterpretCastExpr(
548 CXXReinterpretCastExpr
const * expr
)
550 if (ignoreLocation(expr
)) {
553 if (auto const sub
= dyn_cast
<ImplicitCastExpr
>(expr
->getSubExpr())) {
554 if (sub
->getCastKind() == CK_ArrayToPointerDecay
&& sub
->getType() == expr
->getType())
555 //TODO: instead of exact QualType match, allow some variation?
557 if (loplugin::TypeCheck(sub
->getType()).Pointer().Const().Char()) {
558 if (auto const lit
= dyn_cast
<clang::StringLiteral
>(expr
->getSubExprAsWritten())) {
559 if (lit
->getKind() == clang::StringLiteral::UTF8
) {
562 // redundant_cast<char const *>(u8"...")
564 // in pre-C++2a code:
570 DiagnosticsEngine::Warning
, "redundant reinterpret_cast from %0 to %1",
572 << expr
->getSubExprAsWritten()->getType() << expr
->getTypeAsWritten()
573 << expr
->getSourceRange();
577 if (expr
->getSubExpr()->getType()->isVoidPointerType()) {
578 auto t
= expr
->getType()->getAs
<clang::PointerType
>();
579 if (t
== nullptr || !t
->getPointeeType()->isObjectType()) {
582 if (rewriter
!= nullptr) {
583 auto loc
= compat::getBeginLoc(expr
);
584 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
585 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(
588 if (compiler
.getSourceManager().isMacroBodyExpansion(loc
)) {
589 auto loc2
= compat::getEndLoc(expr
);
590 while (compiler
.getSourceManager().isMacroArgExpansion(loc2
)) {
591 loc2
= compiler
.getSourceManager()
592 .getImmediateMacroCallerLoc(loc2
);
594 if (compiler
.getSourceManager().isMacroBodyExpansion(loc2
)) {
595 //TODO: check loc, loc2 are in same macro body expansion
596 loc
= compiler
.getSourceManager().getSpellingLoc(loc
);
599 auto s
= compiler
.getSourceManager().getCharacterData(loc
);
600 auto n
= Lexer::MeasureTokenLength(
601 loc
, compiler
.getSourceManager(), compiler
.getLangOpts());
602 std::string
tok(s
, n
);
603 if (tok
== "reinterpret_cast" && replaceText(loc
, n
, "static_cast"))
609 DiagnosticsEngine::Warning
,
610 "reinterpret_cast from %0 to %1 can be simplified to static_cast",
612 << expr
->getSubExprAsWritten()->getType() << expr
->getType()
613 << expr
->getSourceRange();
614 } else if (expr
->getType()->isVoidPointerType()) {
615 auto t
= expr
->getSubExpr()->getType()->getAs
<clang::PointerType
>();
616 if (t
== nullptr || !t
->getPointeeType()->isObjectType()) {
620 DiagnosticsEngine::Warning
,
621 ("reinterpret_cast from %0 to %1 can be simplified to static_cast"
622 " or an implicit conversion"),
624 << expr
->getSubExprAsWritten()->getType() << expr
->getType()
625 << expr
->getSourceRange();
626 } else if (expr
->getType()->isFundamentalType()) {
627 if (auto const sub
= dyn_cast
<CXXConstCastExpr
>(
628 expr
->getSubExpr()->IgnoreParens()))
631 DiagnosticsEngine::Warning
,
632 ("redundant const_cast from %0 to %1 within reinterpret_cast to"
633 " fundamental type %2"),
635 << sub
->getSubExprAsWritten()->getType()
636 << sub
->getTypeAsWritten() << expr
->getTypeAsWritten()
637 << expr
->getSourceRange();
644 bool RedundantCast::VisitCXXConstCastExpr(CXXConstCastExpr
const * expr
) {
645 if (ignoreLocation(expr
)) {
648 auto const sub
= compat::getSubExprAsWritten(expr
);
649 if (isRedundantConstCast(expr
)) {
651 DiagnosticsEngine::Warning
,
652 "redundant const_cast from %0 %1 to %2 %3", expr
->getExprLoc())
653 << sub
->getType() << printExprValueKind(sub
->getValueKind())
654 << expr
->getTypeAsWritten()
655 << printExprValueKind(expr
->getValueKind())
656 << expr
->getSourceRange();
659 if (auto const dce
= dyn_cast
<CXXStaticCastExpr
>(
660 sub
->IgnoreParenImpCasts()))
662 auto const sub2
= compat::getSubExprAsWritten(dce
);
663 auto t1
= sub2
->getType().getCanonicalType();
664 auto isNullptr
= t1
->isNullPtrType();
665 auto t2
= dce
->getType().getCanonicalType();
666 auto t3
= expr
->getType().getCanonicalType();
667 auto redundant
= false;
669 if ((t2
.isConstQualified()
670 && (isNullptr
|| !t1
.isConstQualified())
671 && !t3
.isConstQualified())
672 || (t2
.isVolatileQualified()
673 && (isNullptr
|| !t1
.isVolatileQualified())
674 && !t3
.isVolatileQualified()))
680 auto const p1
= t1
->getAs
<clang::PointerType
>();
684 t1
= p1
->getPointeeType();
685 isNullptr
= t1
->isNullPtrType();
687 auto const p2
= t2
->getAs
<clang::PointerType
>();
691 t2
= p2
->getPointeeType();
692 auto const p3
= t3
->getAs
<clang::PointerType
>();
696 t3
= p3
->getPointeeType();
700 DiagnosticsEngine::Warning
,
701 ("redundant static_cast/const_cast combination from %0 via %1"
704 << sub2
->getType() << dce
->getTypeAsWritten()
705 << expr
->getTypeAsWritten() << expr
->getSourceRange();
711 bool RedundantCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
const * expr
) {
712 if (ignoreLocation(expr
)) {
715 // Restrict this to "real" casts (compared to uses of braced-init-list, like
721 // std::initializer_list<Foo>{bar, baz}
723 // ), and only to cases where the sub-expression already is a prvalue of
724 // non-class type (and thus the cast is unlikely to be meant to create a
726 auto const t1
= expr
->getTypeAsWritten();
727 bool const fnptr
= t1
->isFunctionPointerType() || t1
->isMemberFunctionPointerType();
728 auto const sub
= fnptr
? stopAtFunctionPointerDecay(expr
) : compat::getSubExprAsWritten(expr
);
729 if ((sub
->getValueKind() != VK_RValue
&& !fnptr
) || expr
->getType()->isRecordType()
730 || isa
<InitListExpr
>(sub
) || isa
<CXXStdInitializerListExpr
>(sub
))
735 // See "There might even be good reasons(?) not to warn inside explicit
736 // casts" block in compilerplugins/clang/test/cppunitassertequals.cxx:
737 auto const eloc
= expr
->getExprLoc();
738 if (compiler
.getSourceManager().isMacroArgExpansion(eloc
)) {
739 auto const name
= Lexer::getImmediateMacroName(
740 eloc
, compiler
.getSourceManager(), compiler
.getLangOpts());
741 if (name
== "CPPUNIT_ASSERT" || name
== "CPPUNIT_ASSERT_MESSAGE") {
746 // Don't warn if a functional cast on a pointer to function or member function is used to
747 // disambiguate an overloaded function:
749 auto e
= sub
->IgnoreParenImpCasts();
750 if (auto const e1
= dyn_cast
<UnaryOperator
>(e
)) {
751 if (e1
->getOpcode() == UO_AddrOf
) {
752 e
= e1
->getSubExpr()->IgnoreParenImpCasts();
755 if (auto const e1
= dyn_cast
<DeclRefExpr
>(e
)) {
756 if (auto const fdecl
= dyn_cast
<FunctionDecl
>(e1
->getDecl())) {
757 if (isOverloadedFunction(fdecl
)) {
764 // See the commit message of d0e7d020fa405ab94f19916ec96fbd4611da0031
765 // "socket.c -> socket.cxx" for the reason to have
767 // bool(FD_ISSET(...))
769 // in sal/osl/unx/socket.cxx:
770 //TODO: Better check that sub is exactly an expansion of FD_ISSET:
771 if (compat::getEndLoc(sub
).isMacroID()) {
772 for (auto loc
= compat::getBeginLoc(sub
);
774 && (compiler
.getSourceManager()
775 .isAtStartOfImmediateMacroExpansion(loc
));
776 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
))
778 if (Lexer::getImmediateMacroName(
779 loc
, compiler
.getSourceManager(), compiler
.getLangOpts())
787 auto const t2
= sub
->getType();
788 if (t1
.getCanonicalType() != t2
.getCanonicalType())
790 if (!loplugin::isOkToRemoveArithmeticCast(compiler
.getASTContext(), t1
, t2
, expr
->getSubExpr()))
793 DiagnosticsEngine::Warning
,
794 "redundant functional cast from %0 to %1", expr
->getExprLoc())
795 << t2
<< t1
<< expr
->getSourceRange();
799 bool RedundantCast::VisitCXXDynamicCastExpr(CXXDynamicCastExpr
const * expr
) {
800 if (ignoreLocation(expr
)) {
803 // so far this only deals with dynamic casting from T to T
804 auto const sub
= compat::getSubExprAsWritten(expr
);
805 auto const t1
= expr
->getTypeAsWritten();
806 auto const t2
= sub
->getType();
807 if (t1
.getCanonicalType() != t2
.getCanonicalType())
810 DiagnosticsEngine::Warning
,
811 "redundant dynamic cast from %0 to %1", expr
->getExprLoc())
812 << t2
<< t1
<< expr
->getSourceRange();
816 bool RedundantCast::VisitCallExpr(CallExpr
const * expr
) {
817 if (ignoreLocation(expr
)) {
820 auto f
= expr
->getDirectCallee();
821 if (f
== nullptr || !f
->isVariadic()
822 || expr
->getNumArgs() <= f
->getNumParams())
826 for (auto i
= f
->getNumParams(); i
!= expr
->getNumArgs(); ++i
) {
827 auto a
= expr
->getArg(i
);
828 if (a
->getType()->isPointerType()) {
829 auto e
= dyn_cast
<CXXConstCastExpr
>(a
->IgnoreParenImpCasts());
832 DiagnosticsEngine::Warning
,
833 "redundant const_cast of variadic function argument",
835 << expr
->getSourceRange();
842 bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr
const * expr
) {
843 if (ignoreLocation(expr
)) {
846 auto e
= dyn_cast
<CXXConstCastExpr
>(
847 expr
->getArgument()->IgnoreParenImpCasts());
850 DiagnosticsEngine::Warning
,
851 "redundant const_cast in delete expression", e
->getExprLoc())
852 << expr
->getSourceRange();
857 bool RedundantCast::visitBinOp(BinaryOperator
const * expr
) {
858 if (ignoreLocation(expr
)) {
861 if (expr
->getLHS()->getType()->isPointerType()
862 && expr
->getRHS()->getType()->isPointerType())
864 auto e
= dyn_cast
<CXXConstCastExpr
>(
865 expr
->getLHS()->IgnoreParenImpCasts());
868 DiagnosticsEngine::Warning
,
869 "redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
871 << (expr
->getOpcode() == BO_Sub
) << expr
->getSourceRange();
873 e
= dyn_cast
<CXXConstCastExpr
>(
874 expr
->getRHS()->IgnoreParenImpCasts());
877 DiagnosticsEngine::Warning
,
878 "redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
880 << (expr
->getOpcode() == BO_Sub
) << expr
->getSourceRange();
886 bool RedundantCast::isOverloadedFunction(FunctionDecl
const * decl
) {
887 auto const ctx
= decl
->getDeclContext();
888 if (!ctx
->isLookupContext()) {
891 auto const canon
= decl
->getCanonicalDecl();
892 auto const res
= ctx
->lookup(decl
->getDeclName());
893 for (auto d
= res
.begin(); d
!= res
.end(); ++d
) {
894 if (auto const f
= dyn_cast
<FunctionDecl
>(*d
)) {
895 if (f
->getCanonicalDecl() != canon
) {
903 loplugin::Plugin::Registration
<RedundantCast
> X("redundantcast", true);
907 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */