bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / redundantcast.cxx
blob3d9ff97b473b95c7930420bfd09e8839c47fc442
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"
32 namespace {
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);
41 return
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)
52 || (k2 == VK_XValue
53 && (k1 != VK_RValue || from->getType()->isRecordType()));
56 char const * printExprValueKind(ExprValueKind k) {
57 switch (k) {
58 case VK_RValue:
59 return "prvalue";
60 case VK_LValue:
61 return "lvalue";
62 case VK_XValue:
63 return "xvalue";
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;
75 } else {
76 return AlgebraicType::None;
80 class RedundantCast:
81 public loplugin::FilteringRewritePlugin<RedundantCast>
83 public:
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); }
139 private:
140 bool visitBinOp(BinaryOperator const * expr);
141 bool isOverloadedFunction(FunctionDecl const * decl);
144 bool RedundantCast::VisitImplicitCastExpr(const ImplicitCastExpr * expr) {
145 if (ignoreLocation(expr)) {
146 return true;
148 switch (expr->getCastKind()) {
149 case CK_NoOp:
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())))
165 report(
166 DiagnosticsEngine::Warning,
167 ("redundant const_cast from %0 to %1, result is"
168 " implicitly cast to %2"),
169 e->getExprLoc())
170 << e->getSubExprAsWritten()->getType() << e->getType()
171 << expr->getType() << expr->getSourceRange();
175 break;
176 case CK_BitCast:
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()))
188 report(
189 DiagnosticsEngine::Warning,
190 ("redundant const_cast from %0 to %1, result is"
191 " ultimately implicitly cast to %2"),
192 cc->getExprLoc())
193 << cc->getSubExprAsWritten()->getType() << cc->getType()
194 << expr->getType() << expr->getSourceRange();
196 e = cc->getSubExpr()->IgnoreParenImpCasts();
198 if (isa<CXXReinterpretCastExpr>(e)) {
199 report(
200 DiagnosticsEngine::Warning,
201 ("redundant reinterpret_cast, result is implicitly cast to"
202 " void pointer"),
203 e->getExprLoc())
204 << e->getSourceRange();
205 } else if (isa<CXXStaticCastExpr>(e)
206 && isVoidPointer(
207 dyn_cast<CXXStaticCastExpr>(e)->getSubExpr()
208 ->IgnoreParenImpCasts()->getType())
209 && !compiler.getSourceManager().isMacroBodyExpansion(
210 compat::getBeginLoc(e)))
212 report(
213 DiagnosticsEngine::Warning,
214 ("redundant static_cast from void pointer, result is"
215 " implicitly cast to void pointer"),
216 e->getExprLoc())
217 << e->getSourceRange();
220 break;
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()))
232 report(
233 DiagnosticsEngine::Warning,
234 ("redundant const_cast from %0 to %1, result is"
235 " ultimately implicitly cast to %2"),
236 cc->getExprLoc())
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()))
251 report(
252 DiagnosticsEngine::Warning,
253 ("redundant const_cast from %0 to %1, result is"
254 " ultimately implicitly cast to %2"),
255 cc->getExprLoc())
256 << cc->getSubExprAsWritten()->getType() << cc->getType()
257 << expr->getType() << expr->getSourceRange();
259 e = cc->getSubExpr()->IgnoreParenImpCasts();
262 break;
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())))
270 report(
271 DiagnosticsEngine::Warning,
272 ("suspicious %select{static_cast|functional cast}0 from %1 to %2, result is"
273 " implicitly cast to %3"),
274 e->getExprLoc())
275 << isa<CXXFunctionalCastExpr>(e) << e->getSubExprAsWritten()->getType()
276 << e->getTypeAsWritten() << expr->getType() << expr->getSourceRange();
279 break;
280 default:
281 break;
283 return true;
286 bool RedundantCast::VisitCStyleCastExpr(CStyleCastExpr const * expr) {
287 if (ignoreLocation(expr)) {
288 return true;
290 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr)))) {
291 return true;
293 auto t1 = compat::getSubExprAsWritten(expr)->getType();
294 auto t2 = expr->getTypeAsWritten();
295 if (auto templateType = dyn_cast<SubstTemplateTypeParmType>(t1)) {
296 t1 = templateType->desugar();
298 if (t1 != t2) {
299 return true;
301 if (!t1->isBuiltinType() && !loplugin::TypeCheck(t1).Enum() && !loplugin::TypeCheck(t1).Typedef()) {
302 return true;
304 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
306 return true;
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)))
328 return true;
330 report(
331 DiagnosticsEngine::Warning,
332 "redundant cstyle cast from %0 to %1", expr->getExprLoc())
333 << t1 << t2 << expr->getSourceRange();
334 return true;
337 bool RedundantCast::VisitCXXStaticCastExpr(CXXStaticCastExpr const * expr) {
338 if (ignoreLocation(expr)) {
339 return true;
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()) {
348 report(
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"
352 " qualifiers}4"),
353 expr->getExprLoc())
354 << t1 << printExprValueKind(sub->getValueKind())
355 << t2 << printExprValueKind(expr->getValueKind())
356 << ((t2.isLocalConstQualified() ? 1 : 0)
357 + (t2.isLocalVolatileQualified() ? 2 : 0) - 1)
358 << expr->getSourceRange();
359 return true;
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")))
375 report(
376 DiagnosticsEngine::Warning, "redundant static_cast from %0 to %1",
377 expr->getExprLoc())
378 << expr->getSubExprAsWritten()->getType() << t2 << expr->getSourceRange();
380 return true;
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)))
395 return true;
397 report(
398 DiagnosticsEngine::Warning,
399 "static_cast from %0 %1 to %2 %3 should be written as const_cast",
400 expr->getExprLoc())
401 << t1 << printExprValueKind(sub->getValueKind())
402 << t2 << printExprValueKind(expr->getValueKind())
403 << expr->getSourceRange();
404 return true;
406 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
408 return true;
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) {
418 return true;
420 } else if (auto const td2 = t2->getAs<TypedefType>()) {
421 auto const td1 = t1->getAs<TypedefType>();
422 if (td1 == nullptr || td1 != td2) {
423 return true;
425 } else {
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) {
431 return true;
433 } else if (auto const ptd2 = pt2->getAs<TypedefType>()) {
434 auto const ptd1 = pt1->getAs<TypedefType>();
435 if (ptd1 == nullptr || ptd1 != ptd2) {
436 return true;
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))
446 return true;
448 // Don't warn if a static_cast on a pointer to function or member function is used to
449 // disambiguate an overloaded function:
450 if (fnptr) {
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)) {
460 return true;
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())
474 == "assert"))
476 return true;
479 report(
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"),
483 expr->getExprLoc())
484 << t1 << printExprValueKind(k1) << t2 << printExprValueKind(k3)
485 << (k3 == VK_RValue && (k1 != VK_RValue || t1->isRecordType()))
486 << expr->getSourceRange();
487 return true;
490 bool RedundantCast::VisitCXXReinterpretCastExpr(
491 CXXReinterpretCastExpr const * expr)
493 if (ignoreLocation(expr)) {
494 return true;
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) {
503 // Don't warn about
505 // redundant_cast<char const *>(u8"...")
507 // in pre-C++2a code:
508 return true;
512 report(
513 DiagnosticsEngine::Warning, "redundant reinterpret_cast from %0 to %1",
514 expr->getExprLoc())
515 << expr->getSubExprAsWritten()->getType() << expr->getTypeAsWritten()
516 << expr->getSourceRange();
517 return true;
520 if (expr->getSubExpr()->getType()->isVoidPointerType()) {
521 auto t = expr->getType()->getAs<clang::PointerType>();
522 if (t == nullptr || !t->getPointeeType()->isObjectType()) {
523 return true;
525 if (rewriter != nullptr) {
526 auto loc = compat::getBeginLoc(expr);
527 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
528 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(
529 loc);
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"))
548 return true;
551 report(
552 DiagnosticsEngine::Warning,
553 "reinterpret_cast from %0 to %1 can be simplified to static_cast",
554 expr->getExprLoc())
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()) {
560 return true;
562 report(
563 DiagnosticsEngine::Warning,
564 ("reinterpret_cast from %0 to %1 can be simplified to static_cast"
565 " or an implicit conversion"),
566 expr->getExprLoc())
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()))
573 report(
574 DiagnosticsEngine::Warning,
575 ("redundant const_cast from %0 to %1 within reinterpret_cast to"
576 " fundamental type %2"),
577 expr->getExprLoc())
578 << sub->getSubExprAsWritten()->getType()
579 << sub->getTypeAsWritten() << expr->getTypeAsWritten()
580 << expr->getSourceRange();
581 return true;
584 return true;
587 bool RedundantCast::VisitCXXConstCastExpr(CXXConstCastExpr const * expr) {
588 if (ignoreLocation(expr)) {
589 return true;
591 auto const sub = compat::getSubExprAsWritten(expr);
592 if (isRedundantConstCast(expr)) {
593 report(
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();
600 return true;
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;
611 for (;;) {
612 if ((t2.isConstQualified()
613 && (isNullptr || !t1.isConstQualified())
614 && !t3.isConstQualified())
615 || (t2.isVolatileQualified()
616 && (isNullptr || !t1.isVolatileQualified())
617 && !t3.isVolatileQualified()))
619 redundant = true;
620 break;
622 if (!isNullptr) {
623 auto const p1 = t1->getAs<clang::PointerType>();
624 if (p1 == nullptr) {
625 break;
627 t1 = p1->getPointeeType();
628 isNullptr = t1->isNullPtrType();
630 auto const p2 = t2->getAs<clang::PointerType>();
631 if (p2 == nullptr) {
632 break;
634 t2 = p2->getPointeeType();
635 auto const p3 = t3->getAs<clang::PointerType>();
636 if (p3 == nullptr) {
637 break;
639 t3 = p3->getPointeeType();
641 if (redundant) {
642 report(
643 DiagnosticsEngine::Warning,
644 ("redundant static_cast/const_cast combination from %0 via %1"
645 " to %2"),
646 expr->getExprLoc())
647 << sub2->getType() << dce->getTypeAsWritten()
648 << expr->getTypeAsWritten() << expr->getSourceRange();
651 return true;
654 bool RedundantCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr) {
655 if (ignoreLocation(expr)) {
656 return true;
658 // Restrict this to "real" casts (compared to uses of braced-init-list, like
660 // Foo{bar, baz}
662 // or
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
668 // temporary):
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))
675 return true;
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") {
685 return true;
689 // Don't warn if a functional cast on a pointer to function or member function is used to
690 // disambiguate an overloaded function:
691 if (fnptr) {
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)) {
701 return true;
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);
716 loc.isMacroID()
717 && (compiler.getSourceManager()
718 .isAtStartOfImmediateMacroExpansion(loc));
719 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc))
721 if (Lexer::getImmediateMacroName(
722 loc, compiler.getSourceManager(), compiler.getLangOpts())
723 == "FD_ISSET")
725 return true;
730 auto const t2 = sub->getType();
731 if (t1.getCanonicalType() != t2.getCanonicalType())
732 return true;
733 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
734 return true;
735 report(
736 DiagnosticsEngine::Warning,
737 "redundant functional cast from %0 to %1", expr->getExprLoc())
738 << t2 << t1 << expr->getSourceRange();
739 return true;
742 bool RedundantCast::VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr) {
743 if (ignoreLocation(expr)) {
744 return true;
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())
751 return true;
752 report(
753 DiagnosticsEngine::Warning,
754 "redundant dynamic cast from %0 to %1", expr->getExprLoc())
755 << t2 << t1 << expr->getSourceRange();
756 return true;
759 bool RedundantCast::VisitCallExpr(CallExpr const * expr) {
760 if (ignoreLocation(expr)) {
761 return true;
763 auto f = expr->getDirectCallee();
764 if (f == nullptr || !f->isVariadic()
765 || expr->getNumArgs() <= f->getNumParams())
767 return true;
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());
773 if (e != nullptr) {
774 report(
775 DiagnosticsEngine::Warning,
776 "redundant const_cast of variadic function argument",
777 e->getExprLoc())
778 << expr->getSourceRange();
782 return true;
785 bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr const * expr) {
786 if (ignoreLocation(expr)) {
787 return true;
789 auto e = dyn_cast<CXXConstCastExpr>(
790 expr->getArgument()->IgnoreParenImpCasts());
791 if (e != nullptr) {
792 report(
793 DiagnosticsEngine::Warning,
794 "redundant const_cast in delete expression", e->getExprLoc())
795 << expr->getSourceRange();
797 return true;
800 bool RedundantCast::visitBinOp(BinaryOperator const * expr) {
801 if (ignoreLocation(expr)) {
802 return true;
804 if (expr->getLHS()->getType()->isPointerType()
805 && expr->getRHS()->getType()->isPointerType())
807 auto e = dyn_cast<CXXConstCastExpr>(
808 expr->getLHS()->IgnoreParenImpCasts());
809 if (e != nullptr) {
810 report(
811 DiagnosticsEngine::Warning,
812 "redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
813 e->getExprLoc())
814 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
816 e = dyn_cast<CXXConstCastExpr>(
817 expr->getRHS()->IgnoreParenImpCasts());
818 if (e != nullptr) {
819 report(
820 DiagnosticsEngine::Warning,
821 "redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
822 e->getExprLoc())
823 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
826 return true;
829 bool RedundantCast::isOverloadedFunction(FunctionDecl const * decl) {
830 auto const ctx = decl->getDeclContext();
831 if (!ctx->isLookupContext()) {
832 return false;
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) {
839 return true;
843 return false;
846 loplugin::Plugin::Registration<RedundantCast> X("redundantcast", true);
850 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */