nss: upgrade to release 3.73
[LibreOffice.git] / compilerplugins / clang / redundantcast.cxx
blobbdac3f3bcc56a84f5a8940fc22a170ee91175e1b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 // Warn about certain redundant casts:
12 // * A reinterpret_cast<T*>(...) whose result is then implicitly cast to a void
13 // pointer
15 // * A static_cast<T*>(e) where e is of void pointer type and whose result is
16 // then implicitly cast to a void pointer
18 // * Various const_casts that are either not needed (like casting away constness
19 // in a delete expression) or are implicitly cast back afterwards
21 // C-style casts are ignored because it makes this plugin simpler, and they
22 // should eventually be eliminated via loplugin:cstylecast and/or
23 // -Wold-style-cast. That implies that this plugin is only relevant for C++
24 // code.
26 #include "clang/Sema/Sema.h"
28 #include "check.hxx"
29 #include "compat.hxx"
30 #include "plugin.hxx"
31 #include <iostream>
33 namespace {
35 bool isVoidPointer(QualType type) {
36 return type->isPointerType()
37 && type->getAs<clang::PointerType>()->getPointeeType()->isVoidType();
40 bool isRedundantConstCast(CXXConstCastExpr const * expr) {
41 auto const sub = compat::getSubExprAsWritten(expr);
42 return
43 (expr->getType().getCanonicalType()
44 == sub->getType().getCanonicalType())
45 && (expr->getValueKind() != VK_XValue
46 || sub->getValueKind() == VK_XValue);
49 bool canConstCastFromTo(Expr const * from, Expr const * to) {
50 auto const k1 = from->getValueKind();
51 auto const k2 = to->getValueKind();
52 return (k2 == VK_LValue && k1 == VK_LValue)
53 || (k2 == VK_XValue
54 && (k1 != VK_RValue || from->getType()->isRecordType()));
57 char const * printExprValueKind(ExprValueKind k) {
58 switch (k) {
59 case VK_RValue:
60 return "prvalue";
61 case VK_LValue:
62 return "lvalue";
63 case VK_XValue:
64 return "xvalue";
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;
76 } else {
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();
89 return e1;
92 class RedundantCast:
93 public loplugin::FilteringRewritePlugin<RedundantCast>
95 public:
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)) {
137 return true;
139 visitAssign(expr->getLHS()->getType(), expr->getRHS());
140 return true;
142 return true;
145 bool VisitVarDecl(VarDecl const * varDecl);
147 private:
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)) {
161 return true;
163 switch (expr->getCastKind()) {
164 case CK_NoOp:
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())))
180 report(
181 DiagnosticsEngine::Warning,
182 ("redundant const_cast from %0 to %1, result is"
183 " implicitly cast to %2"),
184 e->getExprLoc())
185 << e->getSubExprAsWritten()->getType() << e->getType()
186 << expr->getType() << expr->getSourceRange();
190 break;
191 case CK_BitCast:
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()))
203 report(
204 DiagnosticsEngine::Warning,
205 ("redundant const_cast from %0 to %1, result is"
206 " ultimately implicitly cast to %2"),
207 cc->getExprLoc())
208 << cc->getSubExprAsWritten()->getType() << cc->getType()
209 << expr->getType() << expr->getSourceRange();
211 e = cc->getSubExpr()->IgnoreParenImpCasts();
213 if (isa<CXXReinterpretCastExpr>(e)) {
214 report(
215 DiagnosticsEngine::Warning,
216 ("redundant reinterpret_cast, result is implicitly cast to"
217 " void pointer"),
218 e->getExprLoc())
219 << e->getSourceRange();
220 } else if (isa<CXXStaticCastExpr>(e)
221 && isVoidPointer(
222 dyn_cast<CXXStaticCastExpr>(e)->getSubExpr()
223 ->IgnoreParenImpCasts()->getType())
224 && !compiler.getSourceManager().isMacroBodyExpansion(
225 compat::getBeginLoc(e)))
227 report(
228 DiagnosticsEngine::Warning,
229 ("redundant static_cast from void pointer, result is"
230 " implicitly cast to void pointer"),
231 e->getExprLoc())
232 << e->getSourceRange();
235 break;
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()))
247 report(
248 DiagnosticsEngine::Warning,
249 ("redundant const_cast from %0 to %1, result is"
250 " ultimately implicitly cast to %2"),
251 cc->getExprLoc())
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()))
266 report(
267 DiagnosticsEngine::Warning,
268 ("redundant const_cast from %0 to %1, result is"
269 " ultimately implicitly cast to %2"),
270 cc->getExprLoc())
271 << cc->getSubExprAsWritten()->getType() << cc->getType()
272 << expr->getType() << expr->getSourceRange();
274 e = cc->getSubExpr()->IgnoreParenImpCasts();
277 break;
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())))
285 report(
286 DiagnosticsEngine::Warning,
287 ("suspicious %select{static_cast|functional cast}0 from %1 to %2, result is"
288 " implicitly cast to %3"),
289 e->getExprLoc())
290 << isa<CXXFunctionalCastExpr>(e) << e->getSubExprAsWritten()->getType()
291 << e->getTypeAsWritten() << expr->getType() << expr->getSourceRange();
294 break;
295 default:
296 break;
298 return true;
301 bool RedundantCast::VisitCStyleCastExpr(CStyleCastExpr const * expr) {
302 if (ignoreLocation(expr)) {
303 return true;
305 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr)))) {
306 return true;
308 auto t1 = compat::getSubExprAsWritten(expr)->getType();
309 auto t2 = expr->getTypeAsWritten();
310 if (auto templateType = dyn_cast<SubstTemplateTypeParmType>(t1)) {
311 t1 = templateType->desugar();
313 if (t1 != t2) {
314 return true;
316 if (!t1->isBuiltinType() && !loplugin::TypeCheck(t1).Enum() && !loplugin::TypeCheck(t1).Typedef()) {
317 return true;
319 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
321 return true;
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)))
343 return true;
345 report(
346 DiagnosticsEngine::Warning,
347 "redundant cstyle cast from %0 to %1", expr->getExprLoc())
348 << t1 << t2 << expr->getSourceRange();
349 return true;
352 bool RedundantCast::VisitVarDecl(VarDecl const * varDecl) {
353 if (ignoreLocation(varDecl)) {
354 return true;
356 if (!varDecl->getInit())
357 return true;
358 visitAssign(varDecl->getType(), varDecl->getInit());
359 return true;
362 void RedundantCast::visitAssign(QualType t1, Expr const * rhs)
364 auto staticCastExpr = dyn_cast<CXXStaticCastExpr>(rhs->IgnoreImplicit());
365 if (!staticCastExpr)
366 return;
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;
376 else
378 auto pointee1 = t1->getPointeeCXXRecordDecl();
379 auto pointee2 = t2->getPointeeCXXRecordDecl();
380 if (pointee1 && pointee2)
381 foundOne = loplugin::derivedFromCount(pointee2, pointee1) == 1;
384 if (foundOne)
386 report(
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)) {
395 return true;
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()) {
404 report(
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"
408 " qualifiers}4"),
409 expr->getExprLoc())
410 << t1 << printExprValueKind(sub->getValueKind())
411 << t2 << printExprValueKind(expr->getValueKind())
412 << ((t2.isLocalConstQualified() ? 1 : 0)
413 + (t2.isLocalVolatileQualified() ? 2 : 0) - 1)
414 << expr->getSourceRange();
415 return true;
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)))
432 report(
433 DiagnosticsEngine::Warning, "redundant static_cast from %0 to %1",
434 expr->getExprLoc())
435 << expr->getSubExprAsWritten()->getType() << t2 << expr->getSourceRange();
437 return true;
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)))
452 return true;
454 report(
455 DiagnosticsEngine::Warning,
456 "static_cast from %0 %1 to %2 %3 should be written as const_cast",
457 expr->getExprLoc())
458 << t1 << printExprValueKind(sub->getValueKind())
459 << t2 << printExprValueKind(expr->getValueKind())
460 << expr->getSourceRange();
461 return true;
463 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
465 return true;
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) {
475 return true;
477 } else if (auto const td2 = t2->getAs<TypedefType>()) {
478 auto const td1 = t1->getAs<TypedefType>();
479 if (td1 == nullptr || td1 != td2) {
480 return true;
482 } else {
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) {
488 return true;
490 } else if (auto const ptd2 = pt2->getAs<TypedefType>()) {
491 auto const ptd1 = pt1->getAs<TypedefType>();
492 if (ptd1 == nullptr || ptd1 != ptd2) {
493 return true;
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))
503 return true;
505 // Don't warn if a static_cast on a pointer to function or member function is used to
506 // disambiguate an overloaded function:
507 if (fnptr) {
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)) {
517 return true;
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())
531 == "assert"))
533 return true;
536 report(
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"),
540 expr->getExprLoc())
541 << t1 << printExprValueKind(k1) << t2 << printExprValueKind(k3)
542 << (k3 == VK_RValue && (k1 != VK_RValue || t1->isRecordType()))
543 << expr->getSourceRange();
544 return true;
547 bool RedundantCast::VisitCXXReinterpretCastExpr(
548 CXXReinterpretCastExpr const * expr)
550 if (ignoreLocation(expr)) {
551 return true;
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) {
560 // Don't warn about
562 // redundant_cast<char const *>(u8"...")
564 // in pre-C++2a code:
565 return true;
569 report(
570 DiagnosticsEngine::Warning, "redundant reinterpret_cast from %0 to %1",
571 expr->getExprLoc())
572 << expr->getSubExprAsWritten()->getType() << expr->getTypeAsWritten()
573 << expr->getSourceRange();
574 return true;
577 if (expr->getSubExpr()->getType()->isVoidPointerType()) {
578 auto t = expr->getType()->getAs<clang::PointerType>();
579 if (t == nullptr || !t->getPointeeType()->isObjectType()) {
580 return true;
582 if (rewriter != nullptr) {
583 auto loc = compat::getBeginLoc(expr);
584 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
585 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(
586 loc);
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"))
605 return true;
608 report(
609 DiagnosticsEngine::Warning,
610 "reinterpret_cast from %0 to %1 can be simplified to static_cast",
611 expr->getExprLoc())
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()) {
617 return true;
619 report(
620 DiagnosticsEngine::Warning,
621 ("reinterpret_cast from %0 to %1 can be simplified to static_cast"
622 " or an implicit conversion"),
623 expr->getExprLoc())
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()))
630 report(
631 DiagnosticsEngine::Warning,
632 ("redundant const_cast from %0 to %1 within reinterpret_cast to"
633 " fundamental type %2"),
634 expr->getExprLoc())
635 << sub->getSubExprAsWritten()->getType()
636 << sub->getTypeAsWritten() << expr->getTypeAsWritten()
637 << expr->getSourceRange();
638 return true;
641 return true;
644 bool RedundantCast::VisitCXXConstCastExpr(CXXConstCastExpr const * expr) {
645 if (ignoreLocation(expr)) {
646 return true;
648 auto const sub = compat::getSubExprAsWritten(expr);
649 if (isRedundantConstCast(expr)) {
650 report(
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();
657 return true;
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;
668 for (;;) {
669 if ((t2.isConstQualified()
670 && (isNullptr || !t1.isConstQualified())
671 && !t3.isConstQualified())
672 || (t2.isVolatileQualified()
673 && (isNullptr || !t1.isVolatileQualified())
674 && !t3.isVolatileQualified()))
676 redundant = true;
677 break;
679 if (!isNullptr) {
680 auto const p1 = t1->getAs<clang::PointerType>();
681 if (p1 == nullptr) {
682 break;
684 t1 = p1->getPointeeType();
685 isNullptr = t1->isNullPtrType();
687 auto const p2 = t2->getAs<clang::PointerType>();
688 if (p2 == nullptr) {
689 break;
691 t2 = p2->getPointeeType();
692 auto const p3 = t3->getAs<clang::PointerType>();
693 if (p3 == nullptr) {
694 break;
696 t3 = p3->getPointeeType();
698 if (redundant) {
699 report(
700 DiagnosticsEngine::Warning,
701 ("redundant static_cast/const_cast combination from %0 via %1"
702 " to %2"),
703 expr->getExprLoc())
704 << sub2->getType() << dce->getTypeAsWritten()
705 << expr->getTypeAsWritten() << expr->getSourceRange();
708 return true;
711 bool RedundantCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr) {
712 if (ignoreLocation(expr)) {
713 return true;
715 // Restrict this to "real" casts (compared to uses of braced-init-list, like
717 // Foo{bar, baz}
719 // or
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
725 // temporary):
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))
732 return true;
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") {
742 return true;
746 // Don't warn if a functional cast on a pointer to function or member function is used to
747 // disambiguate an overloaded function:
748 if (fnptr) {
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)) {
758 return true;
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);
773 loc.isMacroID()
774 && (compiler.getSourceManager()
775 .isAtStartOfImmediateMacroExpansion(loc));
776 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc))
778 if (Lexer::getImmediateMacroName(
779 loc, compiler.getSourceManager(), compiler.getLangOpts())
780 == "FD_ISSET")
782 return true;
787 auto const t2 = sub->getType();
788 if (t1.getCanonicalType() != t2.getCanonicalType())
789 return true;
790 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
791 return true;
792 report(
793 DiagnosticsEngine::Warning,
794 "redundant functional cast from %0 to %1", expr->getExprLoc())
795 << t2 << t1 << expr->getSourceRange();
796 return true;
799 bool RedundantCast::VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr) {
800 if (ignoreLocation(expr)) {
801 return true;
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())
808 return true;
809 report(
810 DiagnosticsEngine::Warning,
811 "redundant dynamic cast from %0 to %1", expr->getExprLoc())
812 << t2 << t1 << expr->getSourceRange();
813 return true;
816 bool RedundantCast::VisitCallExpr(CallExpr const * expr) {
817 if (ignoreLocation(expr)) {
818 return true;
820 auto f = expr->getDirectCallee();
821 if (f == nullptr || !f->isVariadic()
822 || expr->getNumArgs() <= f->getNumParams())
824 return true;
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());
830 if (e != nullptr) {
831 report(
832 DiagnosticsEngine::Warning,
833 "redundant const_cast of variadic function argument",
834 e->getExprLoc())
835 << expr->getSourceRange();
839 return true;
842 bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr const * expr) {
843 if (ignoreLocation(expr)) {
844 return true;
846 auto e = dyn_cast<CXXConstCastExpr>(
847 expr->getArgument()->IgnoreParenImpCasts());
848 if (e != nullptr) {
849 report(
850 DiagnosticsEngine::Warning,
851 "redundant const_cast in delete expression", e->getExprLoc())
852 << expr->getSourceRange();
854 return true;
857 bool RedundantCast::visitBinOp(BinaryOperator const * expr) {
858 if (ignoreLocation(expr)) {
859 return true;
861 if (expr->getLHS()->getType()->isPointerType()
862 && expr->getRHS()->getType()->isPointerType())
864 auto e = dyn_cast<CXXConstCastExpr>(
865 expr->getLHS()->IgnoreParenImpCasts());
866 if (e != nullptr) {
867 report(
868 DiagnosticsEngine::Warning,
869 "redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
870 e->getExprLoc())
871 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
873 e = dyn_cast<CXXConstCastExpr>(
874 expr->getRHS()->IgnoreParenImpCasts());
875 if (e != nullptr) {
876 report(
877 DiagnosticsEngine::Warning,
878 "redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
879 e->getExprLoc())
880 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
883 return true;
886 bool RedundantCast::isOverloadedFunction(FunctionDecl const * decl) {
887 auto const ctx = decl->getDeclContext();
888 if (!ctx->isLookupContext()) {
889 return false;
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) {
896 return true;
900 return false;
903 loplugin::Plugin::Registration<RedundantCast> X("redundantcast", true);
907 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */