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"
34 bool isVoidPointer(QualType type
) {
35 return type
->isPointerType()
36 && type
->getAs
<clang::PointerType
>()->getPointeeType()->isVoidType();
39 bool isRedundantConstCast(CXXConstCastExpr
const * expr
) {
40 auto const sub
= compat::getSubExprAsWritten(expr
);
42 (expr
->getType().getCanonicalType()
43 == sub
->getType().getCanonicalType())
44 && (expr
->getValueKind() != VK_XValue
45 || sub
->getValueKind() == VK_XValue
);
48 bool canConstCastFromTo(Expr
const * from
, Expr
const * to
) {
49 auto const k1
= from
->getValueKind();
50 auto const k2
= to
->getValueKind();
51 return (k2
== VK_LValue
&& k1
== VK_LValue
)
53 && (k1
!= VK_RValue
|| from
->getType()->isRecordType()));
56 char const * printExprValueKind(ExprValueKind k
) {
65 llvm_unreachable("unknown ExprValueKind");
68 enum class AlgebraicType
{ None
, Integer
, FloatingPoint
};
70 AlgebraicType
algebraicType(clang::Type
const & type
) {
71 if (type
.isIntegralOrEnumerationType()) {
72 return AlgebraicType::Integer
;
73 } else if (type
.isRealFloatingType()) {
74 return AlgebraicType::FloatingPoint
;
76 return AlgebraicType::None
;
81 public loplugin::FilteringRewritePlugin
<RedundantCast
>
84 explicit RedundantCast(loplugin::InstantiationData
const & data
):
85 FilteringRewritePlugin(data
)
88 virtual void run() override
{
89 if (compiler
.getLangOpts().CPlusPlus
) {
90 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
94 bool TraverseInitListExpr(InitListExpr
* expr
, DataRecursionQueue
* queue
= nullptr) {
95 return WalkUpFromInitListExpr(expr
)
96 && TraverseSynOrSemInitListExpr(
97 expr
->isSemanticForm() ? expr
: expr
->getSemanticForm(), queue
);
100 bool VisitImplicitCastExpr(ImplicitCastExpr
const * expr
);
102 bool VisitCXXStaticCastExpr(CXXStaticCastExpr
const * expr
);
104 bool VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr
const * expr
);
106 bool VisitCXXConstCastExpr(CXXConstCastExpr
const * expr
);
108 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
const * expr
);
110 bool VisitCXXDynamicCastExpr(CXXDynamicCastExpr
const * expr
);
112 bool VisitCallExpr(CallExpr
const * expr
);
114 bool VisitCXXDeleteExpr(CXXDeleteExpr
const * expr
);
116 bool VisitCStyleCastExpr(CStyleCastExpr
const * expr
);
118 bool VisitBinSub(BinaryOperator
const * expr
)
119 { return visitBinOp(expr
); }
121 bool VisitBinLT(BinaryOperator
const * expr
)
122 { return visitBinOp(expr
); }
124 bool VisitBinGT(BinaryOperator
const * expr
)
125 { return visitBinOp(expr
); }
127 bool VisitBinLE(BinaryOperator
const * expr
)
128 { return visitBinOp(expr
); }
130 bool VisitBinGE(BinaryOperator
const * expr
)
131 { return visitBinOp(expr
); }
133 bool VisitBinEQ(BinaryOperator
const * expr
)
134 { return visitBinOp(expr
); }
136 bool VisitBinNE(BinaryOperator
const * expr
)
137 { return visitBinOp(expr
); }
140 bool visitBinOp(BinaryOperator
const * expr
);
141 bool isOverloadedFunction(FunctionDecl
const * decl
);
144 bool RedundantCast::VisitImplicitCastExpr(const ImplicitCastExpr
* expr
) {
145 if (ignoreLocation(expr
)) {
148 switch (expr
->getCastKind()) {
150 if (expr
->getType()->isPointerType()
151 || expr
->getType()->isObjectType())
153 auto e
= dyn_cast
<CXXConstCastExpr
>(
154 expr
->getSubExpr()->IgnoreParenImpCasts());
155 if (e
!= nullptr && !isRedundantConstCast(e
)) {
156 auto t1
= e
->getSubExpr()->getType().getCanonicalType();
157 auto t3
= expr
->getType().getCanonicalType();
158 bool ObjCLifetimeConversion
;
159 if (t1
.getTypePtr() == t3
.getTypePtr()
160 || (compiler
.getSema().IsQualificationConversion(
161 t1
, t3
, false, ObjCLifetimeConversion
)
162 && (e
->getType().getCanonicalType().getTypePtr()
163 != t3
.getTypePtr())))
166 DiagnosticsEngine::Warning
,
167 ("redundant const_cast from %0 to %1, result is"
168 " implicitly cast to %2"),
170 << e
->getSubExprAsWritten()->getType() << e
->getType()
171 << expr
->getType() << expr
->getSourceRange();
177 if (isVoidPointer(expr
->getType())
178 && expr
->getSubExpr()->getType()->isPointerType())
180 Expr
const * e
= expr
->getSubExpr()->IgnoreParenImpCasts();
181 while (isa
<CXXConstCastExpr
>(e
)) {
182 auto cc
= dyn_cast
<CXXConstCastExpr
>(e
);
183 if (expr
->getType()->getAs
<clang::PointerType
>()
184 ->getPointeeType().isAtLeastAsQualifiedAs(
185 cc
->getSubExpr()->getType()
186 ->getAs
<clang::PointerType
>()->getPointeeType()))
189 DiagnosticsEngine::Warning
,
190 ("redundant const_cast from %0 to %1, result is"
191 " ultimately implicitly cast to %2"),
193 << cc
->getSubExprAsWritten()->getType() << cc
->getType()
194 << expr
->getType() << expr
->getSourceRange();
196 e
= cc
->getSubExpr()->IgnoreParenImpCasts();
198 if (isa
<CXXReinterpretCastExpr
>(e
)) {
200 DiagnosticsEngine::Warning
,
201 ("redundant reinterpret_cast, result is implicitly cast to"
204 << e
->getSourceRange();
205 } else if (isa
<CXXStaticCastExpr
>(e
)
207 dyn_cast
<CXXStaticCastExpr
>(e
)->getSubExpr()
208 ->IgnoreParenImpCasts()->getType())
209 && !compiler
.getSourceManager().isMacroBodyExpansion(
210 compat::getBeginLoc(e
)))
213 DiagnosticsEngine::Warning
,
214 ("redundant static_cast from void pointer, result is"
215 " implicitly cast to void pointer"),
217 << e
->getSourceRange();
221 case CK_DerivedToBase
:
222 case CK_UncheckedDerivedToBase
:
223 if (expr
->getType()->isPointerType()) {
224 Expr
const * e
= expr
->getSubExpr()->IgnoreParenImpCasts();
225 while (isa
<CXXConstCastExpr
>(e
)) {
226 auto cc
= dyn_cast
<CXXConstCastExpr
>(e
);
227 if (expr
->getType()->getAs
<clang::PointerType
>()
228 ->getPointeeType().isAtLeastAsQualifiedAs(
229 cc
->getSubExpr()->getType()
230 ->getAs
<clang::PointerType
>()->getPointeeType()))
233 DiagnosticsEngine::Warning
,
234 ("redundant const_cast from %0 to %1, result is"
235 " ultimately implicitly cast to %2"),
237 << cc
->getSubExprAsWritten()->getType() << cc
->getType()
238 << expr
->getType() << expr
->getSourceRange();
240 e
= cc
->getSubExpr()->IgnoreParenImpCasts();
242 } else if (expr
->getType()->isReferenceType()) {
243 Expr
const * e
= expr
->getSubExpr()->IgnoreParenImpCasts();
244 while (isa
<CXXConstCastExpr
>(e
)) {
245 auto cc
= dyn_cast
<CXXConstCastExpr
>(e
);
246 if (expr
->getType()->getAs
<ReferenceType
>()->getPointeeType()
247 .isAtLeastAsQualifiedAs(
248 cc
->getSubExpr()->getType()
249 ->getAs
<ReferenceType
>()->getPointeeType()))
252 DiagnosticsEngine::Warning
,
253 ("redundant const_cast from %0 to %1, result is"
254 " ultimately implicitly cast to %2"),
256 << cc
->getSubExprAsWritten()->getType() << cc
->getType()
257 << expr
->getType() << expr
->getSourceRange();
259 e
= cc
->getSubExpr()->IgnoreParenImpCasts();
263 case CK_FloatingToIntegral
:
264 case CK_IntegralToFloating
:
265 if (auto e
= dyn_cast
<ExplicitCastExpr
>(expr
->getSubExpr()->IgnoreParenImpCasts())) {
266 if ((isa
<CXXStaticCastExpr
>(e
) || isa
<CXXFunctionalCastExpr
>(e
))
267 && (algebraicType(*e
->getSubExprAsWritten()->getType())
268 == algebraicType(*expr
->getType())))
271 DiagnosticsEngine::Warning
,
272 ("suspicious %select{static_cast|functional cast}0 from %1 to %2, result is"
273 " implicitly cast to %3"),
275 << isa
<CXXFunctionalCastExpr
>(e
) << e
->getSubExprAsWritten()->getType()
276 << e
->getTypeAsWritten() << expr
->getType() << expr
->getSourceRange();
286 bool RedundantCast::VisitCStyleCastExpr(CStyleCastExpr
const * expr
) {
287 if (ignoreLocation(expr
)) {
290 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr
)))) {
293 auto t1
= compat::getSubExprAsWritten(expr
)->getType();
294 auto t2
= expr
->getTypeAsWritten();
295 if (auto templateType
= dyn_cast
<SubstTemplateTypeParmType
>(t1
)) {
296 t1
= templateType
->desugar();
301 if (!t1
->isBuiltinType() && !loplugin::TypeCheck(t1
).Enum() && !loplugin::TypeCheck(t1
).Typedef()) {
304 if (!loplugin::isOkToRemoveArithmeticCast(compiler
.getASTContext(), t1
, t2
, expr
->getSubExpr()))
308 // Ignore FD_ISSET expanding to "...(SOCKET)(fd)..." in some Microsoft
309 // winsock2.h (TODO: improve heuristic of determining that the whole
310 // expr is part of a single macro body expansion):
311 auto l1
= compat::getBeginLoc(expr
);
312 while (compiler
.getSourceManager().isMacroArgExpansion(l1
)) {
313 l1
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l1
);
315 auto l2
= expr
->getExprLoc();
316 while (compiler
.getSourceManager().isMacroArgExpansion(l2
)) {
317 l2
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l2
);
319 auto l3
= compat::getEndLoc(expr
);
320 while (compiler
.getSourceManager().isMacroArgExpansion(l3
)) {
321 l3
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l3
);
323 if (compiler
.getSourceManager().isMacroBodyExpansion(l1
)
324 && compiler
.getSourceManager().isMacroBodyExpansion(l2
)
325 && compiler
.getSourceManager().isMacroBodyExpansion(l3
)
326 && ignoreLocation(compiler
.getSourceManager().getSpellingLoc(l2
)))
331 DiagnosticsEngine::Warning
,
332 "redundant cstyle cast from %0 to %1", expr
->getExprLoc())
333 << t1
<< t2
<< expr
->getSourceRange();
337 bool RedundantCast::VisitCXXStaticCastExpr(CXXStaticCastExpr
const * expr
) {
338 if (ignoreLocation(expr
)) {
341 auto const t2
= expr
->getTypeAsWritten();
342 bool const fnptr
= t2
->isFunctionPointerType() || t2
->isMemberFunctionPointerType();
343 auto const sub
= fnptr
? expr
->getSubExpr() : compat::getSubExprAsWritten(expr
);
344 auto const t1
= sub
->getType();
345 auto const nonClassObjectType
= t2
->isObjectType()
346 && !(t2
->isRecordType() || t2
->isArrayType());
347 if (nonClassObjectType
&& t2
.hasLocalQualifiers()) {
349 DiagnosticsEngine::Warning
,
350 ("in static_cast from %0 %1 to %2 %3, remove redundant top-level"
351 " %select{const qualifier|volatile qualifier|const volatile"
354 << t1
<< printExprValueKind(sub
->getValueKind())
355 << t2
<< printExprValueKind(expr
->getValueKind())
356 << ((t2
.isLocalConstQualified() ? 1 : 0)
357 + (t2
.isLocalVolatileQualified() ? 2 : 0) - 1)
358 << expr
->getSourceRange();
361 if (auto const impl
= dyn_cast
<ImplicitCastExpr
>(expr
->getSubExpr())) {
362 if (impl
->getCastKind() == CK_ArrayToPointerDecay
&& impl
->getType() == t2
)
363 //TODO: instead of exact QualType match, allow some variation?
365 auto const fn
= handler
.getMainFileName();
366 if (!(loplugin::isSamePathname(
367 fn
, SRCDIR
"/sal/qa/rtl/strings/test_ostring_concat.cxx")
368 || loplugin::isSamePathname(
369 fn
, SRCDIR
"/sal/qa/rtl/strings/test_ostring_stringliterals.cxx")
370 || loplugin::isSamePathname(
371 fn
, SRCDIR
"/sal/qa/rtl/strings/test_oustring_concat.cxx")
372 || loplugin::isSamePathname(
373 fn
, SRCDIR
"/sal/qa/rtl/strings/test_oustring_stringliterals.cxx")))
376 DiagnosticsEngine::Warning
, "redundant static_cast from %0 to %1",
378 << expr
->getSubExprAsWritten()->getType() << t2
<< expr
->getSourceRange();
383 auto const t3
= expr
->getType();
384 auto const c1
= t1
.getCanonicalType();
385 auto const c3
= t3
.getCanonicalType();
386 if (nonClassObjectType
|| !canConstCastFromTo(sub
, expr
)
387 ? c1
.getTypePtr() != c3
.getTypePtr() : c1
!= c3
)
389 bool ObjCLifetimeConversion
;
390 if (nonClassObjectType
391 || (c1
.getTypePtr() != c3
.getTypePtr()
392 && !compiler
.getSema().IsQualificationConversion(
393 c1
, c3
, false, ObjCLifetimeConversion
)))
398 DiagnosticsEngine::Warning
,
399 "static_cast from %0 %1 to %2 %3 should be written as const_cast",
401 << t1
<< printExprValueKind(sub
->getValueKind())
402 << t2
<< printExprValueKind(expr
->getValueKind())
403 << expr
->getSourceRange();
406 if (!loplugin::isOkToRemoveArithmeticCast(compiler
.getASTContext(), t1
, t2
, expr
->getSubExpr()))
410 // Don't warn if the types are 'void *' and at least one involves a typedef
411 // (and if both involve typedefs, they're different) (this covers cases like
412 // 'oslModule', or 'CURL *', or casts between 'LPVOID' and 'HANDLE' in
413 // Windows-only code):
414 if (loplugin::TypeCheck(t1
).Pointer().NonConstVolatile().Void()) {
415 if (auto const td1
= t1
->getAs
<TypedefType
>()) {
416 auto const td2
= t2
->getAs
<TypedefType
>();
417 if (td2
== nullptr || td2
!= td1
) {
420 } else if (auto const td2
= t2
->getAs
<TypedefType
>()) {
421 auto const td1
= t1
->getAs
<TypedefType
>();
422 if (td1
== nullptr || td1
!= td2
) {
426 auto const pt1
= t1
->getAs
<clang::PointerType
>()->getPointeeType();
427 auto const pt2
= t2
->getAs
<clang::PointerType
>()->getPointeeType();
428 if (auto const ptd1
= pt1
->getAs
<TypedefType
>()) {
429 auto const ptd2
= pt2
->getAs
<TypedefType
>();
430 if (ptd2
== nullptr || ptd2
!= ptd1
) {
433 } else if (auto const ptd2
= pt2
->getAs
<TypedefType
>()) {
434 auto const ptd1
= pt1
->getAs
<TypedefType
>();
435 if (ptd1
== nullptr || ptd1
!= ptd2
) {
441 auto const k1
= sub
->getValueKind();
442 auto const k3
= expr
->getValueKind();
443 if ((k3
== VK_XValue
&& k1
!= VK_XValue
)
444 || (k3
== VK_LValue
&& k1
== VK_XValue
))
448 // Don't warn if a static_cast on a pointer to function or member function is used to
449 // disambiguate an overloaded function:
451 auto e
= sub
->IgnoreParenImpCasts();
452 if (auto const e1
= dyn_cast
<UnaryOperator
>(e
)) {
453 if (e1
->getOpcode() == UO_AddrOf
) {
454 e
= e1
->getSubExpr()->IgnoreParenImpCasts();
457 if (auto const e1
= dyn_cast
<DeclRefExpr
>(e
)) {
458 if (auto const fdecl
= dyn_cast
<FunctionDecl
>(e1
->getDecl())) {
459 if (isOverloadedFunction(fdecl
)) {
465 // Suppress warnings from static_cast<bool> in C++ definition of assert in
466 // <https://sourceware.org/git/?p=glibc.git;a=commit;
467 // h=b5889d25e9bf944a89fdd7bcabf3b6c6f6bb6f7c> "assert: Support types
468 // without operator== (int) [BZ #21972]":
469 if (t1
->isBooleanType() && t2
->isBooleanType()) {
470 auto loc
= compat::getBeginLoc(expr
);
471 if (compiler
.getSourceManager().isMacroBodyExpansion(loc
)
472 && (Lexer::getImmediateMacroName(
473 loc
, compiler
.getSourceManager(), compiler
.getLangOpts())
480 DiagnosticsEngine::Warning
,
481 ("static_cast from %0 %1 to %2 %3 is redundant%select{| or should be"
482 " written as an explicit construction of a temporary}4"),
484 << t1
<< printExprValueKind(k1
) << t2
<< printExprValueKind(k3
)
485 << (k3
== VK_RValue
&& (k1
!= VK_RValue
|| t1
->isRecordType()))
486 << expr
->getSourceRange();
490 bool RedundantCast::VisitCXXReinterpretCastExpr(
491 CXXReinterpretCastExpr
const * expr
)
493 if (ignoreLocation(expr
)) {
496 if (auto const sub
= dyn_cast
<ImplicitCastExpr
>(expr
->getSubExpr())) {
497 if (sub
->getCastKind() == CK_ArrayToPointerDecay
&& sub
->getType() == expr
->getType())
498 //TODO: instead of exact QualType match, allow some variation?
500 if (loplugin::TypeCheck(sub
->getType()).Pointer().Const().Char()) {
501 if (auto const lit
= dyn_cast
<clang::StringLiteral
>(expr
->getSubExprAsWritten())) {
502 if (lit
->getKind() == clang::StringLiteral::UTF8
) {
505 // redundant_cast<char const *>(u8"...")
507 // in pre-C++2a code:
513 DiagnosticsEngine::Warning
, "redundant reinterpret_cast from %0 to %1",
515 << expr
->getSubExprAsWritten()->getType() << expr
->getTypeAsWritten()
516 << expr
->getSourceRange();
520 if (expr
->getSubExpr()->getType()->isVoidPointerType()) {
521 auto t
= expr
->getType()->getAs
<clang::PointerType
>();
522 if (t
== nullptr || !t
->getPointeeType()->isObjectType()) {
525 if (rewriter
!= nullptr) {
526 auto loc
= compat::getBeginLoc(expr
);
527 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
528 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(
531 if (compiler
.getSourceManager().isMacroBodyExpansion(loc
)) {
532 auto loc2
= compat::getEndLoc(expr
);
533 while (compiler
.getSourceManager().isMacroArgExpansion(loc2
)) {
534 loc2
= compiler
.getSourceManager()
535 .getImmediateMacroCallerLoc(loc2
);
537 if (compiler
.getSourceManager().isMacroBodyExpansion(loc2
)) {
538 //TODO: check loc, loc2 are in same macro body expansion
539 loc
= compiler
.getSourceManager().getSpellingLoc(loc
);
542 auto s
= compiler
.getSourceManager().getCharacterData(loc
);
543 auto n
= Lexer::MeasureTokenLength(
544 loc
, compiler
.getSourceManager(), compiler
.getLangOpts());
545 std::string
tok(s
, n
);
546 if (tok
== "reinterpret_cast" && replaceText(loc
, n
, "static_cast"))
552 DiagnosticsEngine::Warning
,
553 "reinterpret_cast from %0 to %1 can be simplified to static_cast",
555 << expr
->getSubExprAsWritten()->getType() << expr
->getType()
556 << expr
->getSourceRange();
557 } else if (expr
->getType()->isVoidPointerType()) {
558 auto t
= expr
->getSubExpr()->getType()->getAs
<clang::PointerType
>();
559 if (t
== nullptr || !t
->getPointeeType()->isObjectType()) {
563 DiagnosticsEngine::Warning
,
564 ("reinterpret_cast from %0 to %1 can be simplified to static_cast"
565 " or an implicit conversion"),
567 << expr
->getSubExprAsWritten()->getType() << expr
->getType()
568 << expr
->getSourceRange();
569 } else if (expr
->getType()->isFundamentalType()) {
570 if (auto const sub
= dyn_cast
<CXXConstCastExpr
>(
571 expr
->getSubExpr()->IgnoreParens()))
574 DiagnosticsEngine::Warning
,
575 ("redundant const_cast from %0 to %1 within reinterpret_cast to"
576 " fundamental type %2"),
578 << sub
->getSubExprAsWritten()->getType()
579 << sub
->getTypeAsWritten() << expr
->getTypeAsWritten()
580 << expr
->getSourceRange();
587 bool RedundantCast::VisitCXXConstCastExpr(CXXConstCastExpr
const * expr
) {
588 if (ignoreLocation(expr
)) {
591 auto const sub
= compat::getSubExprAsWritten(expr
);
592 if (isRedundantConstCast(expr
)) {
594 DiagnosticsEngine::Warning
,
595 "redundant const_cast from %0 %1 to %2 %3", expr
->getExprLoc())
596 << sub
->getType() << printExprValueKind(sub
->getValueKind())
597 << expr
->getTypeAsWritten()
598 << printExprValueKind(expr
->getValueKind())
599 << expr
->getSourceRange();
602 if (auto const dce
= dyn_cast
<CXXStaticCastExpr
>(
603 sub
->IgnoreParenImpCasts()))
605 auto const sub2
= compat::getSubExprAsWritten(dce
);
606 auto t1
= sub2
->getType().getCanonicalType();
607 auto isNullptr
= t1
->isNullPtrType();
608 auto t2
= dce
->getType().getCanonicalType();
609 auto t3
= expr
->getType().getCanonicalType();
610 auto redundant
= false;
612 if ((t2
.isConstQualified()
613 && (isNullptr
|| !t1
.isConstQualified())
614 && !t3
.isConstQualified())
615 || (t2
.isVolatileQualified()
616 && (isNullptr
|| !t1
.isVolatileQualified())
617 && !t3
.isVolatileQualified()))
623 auto const p1
= t1
->getAs
<clang::PointerType
>();
627 t1
= p1
->getPointeeType();
628 isNullptr
= t1
->isNullPtrType();
630 auto const p2
= t2
->getAs
<clang::PointerType
>();
634 t2
= p2
->getPointeeType();
635 auto const p3
= t3
->getAs
<clang::PointerType
>();
639 t3
= p3
->getPointeeType();
643 DiagnosticsEngine::Warning
,
644 ("redundant static_cast/const_cast combination from %0 via %1"
647 << sub2
->getType() << dce
->getTypeAsWritten()
648 << expr
->getTypeAsWritten() << expr
->getSourceRange();
654 bool RedundantCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
const * expr
) {
655 if (ignoreLocation(expr
)) {
658 // Restrict this to "real" casts (compared to uses of braced-init-list, like
664 // std::initializer_list<Foo>{bar, baz}
666 // ), and only to cases where the sub-expression already is a prvalue of
667 // non-class type (and thus the cast is unlikely to be meant to create a
669 auto const t1
= expr
->getTypeAsWritten();
670 bool const fnptr
= t1
->isFunctionPointerType() || t1
->isMemberFunctionPointerType();
671 auto const sub
= fnptr
? expr
->getSubExpr() : compat::getSubExprAsWritten(expr
);
672 if (sub
->getValueKind() != VK_RValue
|| expr
->getType()->isRecordType()
673 || isa
<InitListExpr
>(sub
) || isa
<CXXStdInitializerListExpr
>(sub
))
678 // See "There might even be good reasons(?) not to warn inside explicit
679 // casts" block in compilerplugins/clang/test/cppunitassertequals.cxx:
680 auto const eloc
= expr
->getExprLoc();
681 if (compiler
.getSourceManager().isMacroArgExpansion(eloc
)) {
682 auto const name
= Lexer::getImmediateMacroName(
683 eloc
, compiler
.getSourceManager(), compiler
.getLangOpts());
684 if (name
== "CPPUNIT_ASSERT" || name
== "CPPUNIT_ASSERT_MESSAGE") {
689 // Don't warn if a functional cast on a pointer to function or member function is used to
690 // disambiguate an overloaded function:
692 auto e
= sub
->IgnoreParenImpCasts();
693 if (auto const e1
= dyn_cast
<UnaryOperator
>(e
)) {
694 if (e1
->getOpcode() == UO_AddrOf
) {
695 e
= e1
->getSubExpr()->IgnoreParenImpCasts();
698 if (auto const e1
= dyn_cast
<DeclRefExpr
>(e
)) {
699 if (auto const fdecl
= dyn_cast
<FunctionDecl
>(e1
->getDecl())) {
700 if (isOverloadedFunction(fdecl
)) {
707 // See the commit message of d0e7d020fa405ab94f19916ec96fbd4611da0031
708 // "socket.c -> socket.cxx" for the reason to have
710 // bool(FD_ISSET(...))
712 // in sal/osl/unx/socket.cxx:
713 //TODO: Better check that sub is exactly an expansion of FD_ISSET:
714 if (compat::getEndLoc(sub
).isMacroID()) {
715 for (auto loc
= compat::getBeginLoc(sub
);
717 && (compiler
.getSourceManager()
718 .isAtStartOfImmediateMacroExpansion(loc
));
719 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
))
721 if (Lexer::getImmediateMacroName(
722 loc
, compiler
.getSourceManager(), compiler
.getLangOpts())
730 auto const t2
= sub
->getType();
731 if (t1
.getCanonicalType() != t2
.getCanonicalType())
733 if (!loplugin::isOkToRemoveArithmeticCast(compiler
.getASTContext(), t1
, t2
, expr
->getSubExpr()))
736 DiagnosticsEngine::Warning
,
737 "redundant functional cast from %0 to %1", expr
->getExprLoc())
738 << t2
<< t1
<< expr
->getSourceRange();
742 bool RedundantCast::VisitCXXDynamicCastExpr(CXXDynamicCastExpr
const * expr
) {
743 if (ignoreLocation(expr
)) {
746 // so far this only deals with dynamic casting from T to T
747 auto const sub
= compat::getSubExprAsWritten(expr
);
748 auto const t1
= expr
->getTypeAsWritten();
749 auto const t2
= sub
->getType();
750 if (t1
.getCanonicalType() != t2
.getCanonicalType())
753 DiagnosticsEngine::Warning
,
754 "redundant dynamic cast from %0 to %1", expr
->getExprLoc())
755 << t2
<< t1
<< expr
->getSourceRange();
759 bool RedundantCast::VisitCallExpr(CallExpr
const * expr
) {
760 if (ignoreLocation(expr
)) {
763 auto f
= expr
->getDirectCallee();
764 if (f
== nullptr || !f
->isVariadic()
765 || expr
->getNumArgs() <= f
->getNumParams())
769 for (auto i
= f
->getNumParams(); i
!= expr
->getNumArgs(); ++i
) {
770 auto a
= expr
->getArg(i
);
771 if (a
->getType()->isPointerType()) {
772 auto e
= dyn_cast
<CXXConstCastExpr
>(a
->IgnoreParenImpCasts());
775 DiagnosticsEngine::Warning
,
776 "redundant const_cast of variadic function argument",
778 << expr
->getSourceRange();
785 bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr
const * expr
) {
786 if (ignoreLocation(expr
)) {
789 auto e
= dyn_cast
<CXXConstCastExpr
>(
790 expr
->getArgument()->IgnoreParenImpCasts());
793 DiagnosticsEngine::Warning
,
794 "redundant const_cast in delete expression", e
->getExprLoc())
795 << expr
->getSourceRange();
800 bool RedundantCast::visitBinOp(BinaryOperator
const * expr
) {
801 if (ignoreLocation(expr
)) {
804 if (expr
->getLHS()->getType()->isPointerType()
805 && expr
->getRHS()->getType()->isPointerType())
807 auto e
= dyn_cast
<CXXConstCastExpr
>(
808 expr
->getLHS()->IgnoreParenImpCasts());
811 DiagnosticsEngine::Warning
,
812 "redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
814 << (expr
->getOpcode() == BO_Sub
) << expr
->getSourceRange();
816 e
= dyn_cast
<CXXConstCastExpr
>(
817 expr
->getRHS()->IgnoreParenImpCasts());
820 DiagnosticsEngine::Warning
,
821 "redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
823 << (expr
->getOpcode() == BO_Sub
) << expr
->getSourceRange();
829 bool RedundantCast::isOverloadedFunction(FunctionDecl
const * decl
) {
830 auto const ctx
= decl
->getDeclContext();
831 if (!ctx
->isLookupContext()) {
834 auto const canon
= decl
->getCanonicalDecl();
835 auto const res
= ctx
->lookup(decl
->getDeclName());
836 for (auto d
= res
.begin(); d
!= res
.end(); ++d
) {
837 if (auto const f
= dyn_cast
<FunctionDecl
>(*d
)) {
838 if (f
->getCanonicalDecl() != canon
) {
846 loplugin::Plugin::Registration
<RedundantCast
> X("redundantcast", true);
850 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */