LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / compilerplugins / clang / redundantcast.cxx
blob27566809494c34e0eebc93b8fd3d17e214de62e2
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 != compat::VK_PRValue || from->getType()->isRecordType()));
57 char const * printExprValueKind(ExprValueKind k) {
58 switch (k) {
59 case compat::VK_PRValue:
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 TraverseReturnStmt(ReturnStmt * stmt) {
113 auto const saved = returnExpr_;
114 returnExpr_ = stmt->getRetValue();
115 auto const ret = FilteringRewritePlugin::TraverseReturnStmt(stmt);
116 returnExpr_ = saved;
117 return ret;
120 bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
122 bool VisitCXXStaticCastExpr(CXXStaticCastExpr const * expr);
124 bool VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr const * expr);
126 bool VisitCXXConstCastExpr(CXXConstCastExpr const * expr);
128 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr);
130 bool VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr);
132 bool VisitCallExpr(CallExpr const * expr);
134 bool VisitCXXDeleteExpr(CXXDeleteExpr const * expr);
136 bool VisitCStyleCastExpr(CStyleCastExpr const * expr);
138 bool VisitBinaryOperator(BinaryOperator const * expr) {
139 auto const op = expr->getOpcode();
140 if (op == BO_Sub || expr->isRelationalOp() || expr->isEqualityOp()) {
141 return visitBinOp(expr);
143 if (op == BO_Assign) {
144 if (ignoreLocation(expr)) {
145 return true;
147 visitAssign(expr->getLHS()->getType(), expr->getRHS());
148 return true;
150 return true;
153 bool VisitVarDecl(VarDecl const * varDecl);
155 private:
156 bool visitBinOp(BinaryOperator const * expr);
157 void visitAssign(QualType lhs, Expr const * rhs);
158 bool isOverloadedFunction(FunctionDecl const * decl);
160 bool isInIgnoredMacroBody(Expr const * expr) {
161 auto const loc = compat::getBeginLoc(expr);
162 return compiler.getSourceManager().isMacroBodyExpansion(loc)
163 && ignoreLocation(compiler.getSourceManager().getSpellingLoc(loc));
166 Expr const * returnExpr_ = nullptr;
169 bool RedundantCast::VisitImplicitCastExpr(const ImplicitCastExpr * expr) {
170 if (ignoreLocation(expr)) {
171 return true;
173 switch (expr->getCastKind()) {
174 case CK_NoOp:
175 if (expr->getType()->isPointerType()
176 || expr->getType()->isObjectType())
178 auto e = dyn_cast<CXXConstCastExpr>(
179 expr->getSubExpr()->IgnoreParenImpCasts());
180 if (e != nullptr && !isRedundantConstCast(e)) {
181 auto t1 = e->getSubExpr()->getType().getCanonicalType();
182 auto t3 = expr->getType().getCanonicalType();
183 bool ObjCLifetimeConversion;
184 if (t1.getTypePtr() == t3.getTypePtr()
185 || (compiler.getSema().IsQualificationConversion(
186 t1, t3, false, ObjCLifetimeConversion)
187 && (e->getType().getCanonicalType().getTypePtr()
188 != t3.getTypePtr())))
190 report(
191 DiagnosticsEngine::Warning,
192 ("redundant const_cast from %0 to %1, result is"
193 " implicitly cast to %2"),
194 e->getExprLoc())
195 << e->getSubExprAsWritten()->getType() << e->getType()
196 << expr->getType() << expr->getSourceRange();
200 break;
201 case CK_BitCast:
202 if (isVoidPointer(expr->getType())
203 && expr->getSubExpr()->getType()->isPointerType())
205 Expr const * e = expr->getSubExpr()->IgnoreParenImpCasts();
206 while (isa<CXXConstCastExpr>(e)) {
207 auto cc = dyn_cast<CXXConstCastExpr>(e);
208 if (expr->getType()->getAs<clang::PointerType>()
209 ->getPointeeType().isAtLeastAsQualifiedAs(
210 cc->getSubExpr()->getType()
211 ->getAs<clang::PointerType>()->getPointeeType()))
213 report(
214 DiagnosticsEngine::Warning,
215 ("redundant const_cast from %0 to %1, result is"
216 " ultimately implicitly cast to %2"),
217 cc->getExprLoc())
218 << cc->getSubExprAsWritten()->getType() << cc->getType()
219 << expr->getType() << expr->getSourceRange();
221 e = cc->getSubExpr()->IgnoreParenImpCasts();
223 if (isa<CXXReinterpretCastExpr>(e)) {
224 report(
225 DiagnosticsEngine::Warning,
226 ("redundant reinterpret_cast, result is implicitly cast to"
227 " void pointer"),
228 e->getExprLoc())
229 << e->getSourceRange();
230 } else if (isa<CXXStaticCastExpr>(e)
231 && isVoidPointer(
232 dyn_cast<CXXStaticCastExpr>(e)->getSubExpr()
233 ->IgnoreParenImpCasts()->getType())
234 && !compiler.getSourceManager().isMacroBodyExpansion(
235 compat::getBeginLoc(e)))
237 report(
238 DiagnosticsEngine::Warning,
239 ("redundant static_cast from void pointer, result is"
240 " implicitly cast to void pointer"),
241 e->getExprLoc())
242 << e->getSourceRange();
245 break;
246 case CK_DerivedToBase:
247 case CK_UncheckedDerivedToBase:
248 if (expr->getType()->isPointerType()) {
249 Expr const * e = expr->getSubExpr()->IgnoreParenImpCasts();
250 while (isa<CXXConstCastExpr>(e)) {
251 auto cc = dyn_cast<CXXConstCastExpr>(e);
252 if (expr->getType()->getAs<clang::PointerType>()
253 ->getPointeeType().isAtLeastAsQualifiedAs(
254 cc->getSubExpr()->getType()
255 ->getAs<clang::PointerType>()->getPointeeType()))
257 report(
258 DiagnosticsEngine::Warning,
259 ("redundant const_cast from %0 to %1, result is"
260 " ultimately implicitly cast to %2"),
261 cc->getExprLoc())
262 << cc->getSubExprAsWritten()->getType() << cc->getType()
263 << expr->getType() << expr->getSourceRange();
265 e = cc->getSubExpr()->IgnoreParenImpCasts();
267 } else if (expr->getType()->isReferenceType()) {
268 Expr const * e = expr->getSubExpr()->IgnoreParenImpCasts();
269 while (isa<CXXConstCastExpr>(e)) {
270 auto cc = dyn_cast<CXXConstCastExpr>(e);
271 if (expr->getType()->getAs<ReferenceType>()->getPointeeType()
272 .isAtLeastAsQualifiedAs(
273 cc->getSubExpr()->getType()
274 ->getAs<ReferenceType>()->getPointeeType()))
276 report(
277 DiagnosticsEngine::Warning,
278 ("redundant const_cast from %0 to %1, result is"
279 " ultimately implicitly cast to %2"),
280 cc->getExprLoc())
281 << cc->getSubExprAsWritten()->getType() << cc->getType()
282 << expr->getType() << expr->getSourceRange();
284 e = cc->getSubExpr()->IgnoreParenImpCasts();
287 break;
288 case CK_FloatingToIntegral:
289 case CK_IntegralToFloating:
290 if (auto e = dyn_cast<ExplicitCastExpr>(expr->getSubExpr()->IgnoreParenImpCasts())) {
291 if ((isa<CXXStaticCastExpr>(e) || isa<CXXFunctionalCastExpr>(e))
292 && (algebraicType(*e->getSubExprAsWritten()->getType())
293 == algebraicType(*expr->getType())))
295 report(
296 DiagnosticsEngine::Warning,
297 ("suspicious %select{static_cast|functional cast}0 from %1 to %2, result is"
298 " implicitly cast to %3"),
299 e->getExprLoc())
300 << isa<CXXFunctionalCastExpr>(e) << e->getSubExprAsWritten()->getType()
301 << e->getTypeAsWritten() << expr->getType() << expr->getSourceRange();
304 break;
305 default:
306 break;
308 return true;
311 bool RedundantCast::VisitCStyleCastExpr(CStyleCastExpr const * expr) {
312 if (ignoreLocation(expr)) {
313 return true;
315 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr)))) {
316 return true;
318 auto t1 = compat::getSubExprAsWritten(expr)->getType();
319 auto t2 = expr->getTypeAsWritten();
320 if (auto templateType = dyn_cast<SubstTemplateTypeParmType>(t1)) {
321 t1 = templateType->desugar();
323 if (t1 != t2) {
324 return true;
326 if (!t1->isBuiltinType() && !loplugin::TypeCheck(t1).Enum() && !loplugin::TypeCheck(t1).Typedef()) {
327 return true;
329 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
331 return true;
333 // Ignore FD_ISSET expanding to "...(SOCKET)(fd)..." in some Microsoft
334 // winsock2.h (TODO: improve heuristic of determining that the whole
335 // expr is part of a single macro body expansion):
336 auto l1 = compat::getBeginLoc(expr);
337 while (compiler.getSourceManager().isMacroArgExpansion(l1)) {
338 l1 = compiler.getSourceManager().getImmediateMacroCallerLoc(l1);
340 auto l2 = expr->getExprLoc();
341 while (compiler.getSourceManager().isMacroArgExpansion(l2)) {
342 l2 = compiler.getSourceManager().getImmediateMacroCallerLoc(l2);
344 auto l3 = compat::getEndLoc(expr);
345 while (compiler.getSourceManager().isMacroArgExpansion(l3)) {
346 l3 = compiler.getSourceManager().getImmediateMacroCallerLoc(l3);
348 if (compiler.getSourceManager().isMacroBodyExpansion(l1)
349 && compiler.getSourceManager().isMacroBodyExpansion(l2)
350 && compiler.getSourceManager().isMacroBodyExpansion(l3)
351 && ignoreLocation(compiler.getSourceManager().getSpellingLoc(l2)))
353 return true;
355 report(
356 DiagnosticsEngine::Warning,
357 "redundant cstyle cast from %0 to %1", expr->getExprLoc())
358 << t1 << t2 << expr->getSourceRange();
359 return true;
362 bool RedundantCast::VisitVarDecl(VarDecl const * varDecl) {
363 if (ignoreLocation(varDecl)) {
364 return true;
366 if (!varDecl->getInit())
367 return true;
368 visitAssign(varDecl->getType(), varDecl->getInit());
369 return true;
372 void RedundantCast::visitAssign(QualType t1, Expr const * rhs)
374 auto staticCastExpr = dyn_cast<CXXStaticCastExpr>(rhs->IgnoreImplicit());
375 if (!staticCastExpr)
376 return;
378 auto const t2 = staticCastExpr->getSubExpr()->IgnoreImplicit()->getType();
380 // if there is more than one copy of the LHS, this cast is resolving ambiguity
381 bool foundOne = false;
382 if (t1->isRecordType())
384 foundOne = loplugin::derivedFromCount(t2, t1) == 1;
386 else
388 auto pointee1 = t1->getPointeeCXXRecordDecl();
389 auto pointee2 = t2->getPointeeCXXRecordDecl();
390 if (pointee1 && pointee2)
391 foundOne = loplugin::derivedFromCount(pointee2, pointee1) == 1;
394 if (foundOne)
396 report(
397 DiagnosticsEngine::Warning, "redundant static_cast from %0 to %1",
398 staticCastExpr->getExprLoc())
399 << t2 << t1 << staticCastExpr->getSourceRange();
403 bool RedundantCast::VisitCXXStaticCastExpr(CXXStaticCastExpr const * expr) {
404 if (ignoreLocation(expr)) {
405 return true;
407 auto const t2 = expr->getTypeAsWritten();
408 bool const fnptr = t2->isFunctionPointerType() || t2->isMemberFunctionPointerType();
409 auto const sub = fnptr ? stopAtFunctionPointerDecay(expr) : compat::getSubExprAsWritten(expr);
410 auto const t1 = sub->getType();
411 auto const nonClassObjectType = t2->isObjectType()
412 && !(t2->isRecordType() || t2->isArrayType());
413 if (nonClassObjectType && t2.hasLocalQualifiers()) {
414 report(
415 DiagnosticsEngine::Warning,
416 ("in static_cast from %0 %1 to %2 %3, remove redundant top-level"
417 " %select{const qualifier|volatile qualifier|const volatile"
418 " qualifiers}4"),
419 expr->getExprLoc())
420 << t1 << printExprValueKind(sub->getValueKind())
421 << t2 << printExprValueKind(expr->getValueKind())
422 << ((t2.isLocalConstQualified() ? 1 : 0)
423 + (t2.isLocalVolatileQualified() ? 2 : 0) - 1)
424 << expr->getSourceRange();
425 return true;
427 if (auto const impl = dyn_cast<ImplicitCastExpr>(expr->getSubExpr())) {
428 if (impl->getCastKind() == CK_ArrayToPointerDecay && impl->getType() == t2)
429 //TODO: instead of exact QualType match, allow some variation?
431 auto const fn = handler.getMainFileName();
432 if (!(loplugin::isSamePathname(
433 fn, SRCDIR "/sal/qa/rtl/strings/test_ostring_concat.cxx")
434 || loplugin::isSamePathname(
435 fn, SRCDIR "/sal/qa/rtl/strings/test_ostring_stringliterals.cxx")
436 || loplugin::isSamePathname(
437 fn, SRCDIR "/sal/qa/rtl/strings/test_oustring_concat.cxx")
438 || loplugin::isSamePathname(
439 fn, SRCDIR "/sal/qa/rtl/strings/test_oustring_stringliterals.cxx")
440 || isInIgnoredMacroBody(expr)))
442 report(
443 DiagnosticsEngine::Warning, "redundant static_cast from %0 to %1",
444 expr->getExprLoc())
445 << expr->getSubExprAsWritten()->getType() << t2 << expr->getSourceRange();
447 return true;
450 auto const t3 = expr->getType();
451 auto const c1 = t1.getCanonicalType();
452 auto const c3 = t3.getCanonicalType();
453 if (nonClassObjectType || !canConstCastFromTo(sub, expr)
454 ? c1.getTypePtr() != c3.getTypePtr() : c1 != c3)
456 bool ObjCLifetimeConversion;
457 if (nonClassObjectType
458 || (c1.getTypePtr() != c3.getTypePtr()
459 && !compiler.getSema().IsQualificationConversion(
460 c1, c3, false, ObjCLifetimeConversion)))
462 return true;
464 report(
465 DiagnosticsEngine::Warning,
466 "static_cast from %0 %1 to %2 %3 should be written as const_cast",
467 expr->getExprLoc())
468 << t1 << printExprValueKind(sub->getValueKind())
469 << t2 << printExprValueKind(expr->getValueKind())
470 << expr->getSourceRange();
471 return true;
473 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
475 return true;
477 // Don't warn if the types are 'void *' and at least one involves a typedef
478 // (and if both involve typedefs, they're different) (this covers cases like
479 // 'oslModule', or 'CURL *', or casts between 'LPVOID' and 'HANDLE' in
480 // Windows-only code):
481 if (loplugin::TypeCheck(t1).Pointer().NonConstVolatile().Void()) {
482 if (auto const td1 = t1->getAs<TypedefType>()) {
483 auto const td2 = t2->getAs<TypedefType>();
484 if (td2 == nullptr || td2 != td1) {
485 return true;
487 } else if (auto const td2 = t2->getAs<TypedefType>()) {
488 auto const td1 = t1->getAs<TypedefType>();
489 if (td1 == nullptr || td1 != td2) {
490 return true;
492 } else {
493 auto const pt1 = t1->getAs<clang::PointerType>()->getPointeeType();
494 auto const pt2 = t2->getAs<clang::PointerType>()->getPointeeType();
495 if (auto const ptd1 = pt1->getAs<TypedefType>()) {
496 auto const ptd2 = pt2->getAs<TypedefType>();
497 if (ptd2 == nullptr || ptd2 != ptd1) {
498 return true;
500 } else if (auto const ptd2 = pt2->getAs<TypedefType>()) {
501 auto const ptd1 = pt1->getAs<TypedefType>();
502 if (ptd1 == nullptr || ptd1 != ptd2) {
503 return true;
508 auto const k1 = sub->getValueKind();
509 auto const k3 = expr->getValueKind();
510 if ((k3 == VK_XValue && k1 != VK_XValue)
511 || (k3 == VK_LValue && k1 == VK_XValue))
513 return true;
515 // For <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2266r1.html> "P2266R1: Simpler
516 // implicit move" (as implemented with <https://github.com/llvm/llvm-project/commit/
517 // bf20631782183cd19e0bb7219e908c2bbb01a75f> "[clang] Implement P2266 Simpler implicit move"
518 // towards Clang 13), don't warn about a static_cast in a return statement like
520 // return static_cast<int &>(x);
522 // that needs an lvalue but where in a return statement like
524 // return x;
526 // the expression would now be an xvalue:
527 if (k3 == VK_LValue && k1 == VK_LValue && returnExpr_ != nullptr
528 && expr == returnExpr_->IgnoreParens())
530 return true;
532 // Don't warn if a static_cast on a pointer to function or member function is used to
533 // disambiguate an overloaded function:
534 if (fnptr) {
535 auto e = sub->IgnoreParenImpCasts();
536 if (auto const e1 = dyn_cast<UnaryOperator>(e)) {
537 if (e1->getOpcode() == UO_AddrOf) {
538 e = e1->getSubExpr()->IgnoreParenImpCasts();
541 if (auto const e1 = dyn_cast<DeclRefExpr>(e)) {
542 if (auto const fdecl = dyn_cast<FunctionDecl>(e1->getDecl())) {
543 if (isOverloadedFunction(fdecl)) {
544 return true;
549 // Suppress warnings from static_cast<bool> in C++ definition of assert in
550 // <https://sourceware.org/git/?p=glibc.git;a=commit;
551 // h=b5889d25e9bf944a89fdd7bcabf3b6c6f6bb6f7c> "assert: Support types
552 // without operator== (int) [BZ #21972]":
553 if (t1->isBooleanType() && t2->isBooleanType()) {
554 auto loc = compat::getBeginLoc(expr);
555 if (compiler.getSourceManager().isMacroBodyExpansion(loc)
556 && (Lexer::getImmediateMacroName(
557 loc, compiler.getSourceManager(), compiler.getLangOpts())
558 == "assert"))
560 return true;
563 report(
564 DiagnosticsEngine::Warning,
565 ("static_cast from %0 %1 to %2 %3 is redundant%select{| or should be"
566 " written as an explicit construction of a temporary}4"),
567 expr->getExprLoc())
568 << t1 << printExprValueKind(k1) << t2 << printExprValueKind(k3)
569 << (k3 == compat::VK_PRValue && (k1 != compat::VK_PRValue || t1->isRecordType()))
570 << expr->getSourceRange();
571 return true;
574 bool RedundantCast::VisitCXXReinterpretCastExpr(
575 CXXReinterpretCastExpr const * expr)
577 if (ignoreLocation(expr)) {
578 return true;
580 if (auto const sub = dyn_cast<ImplicitCastExpr>(expr->getSubExpr())) {
581 if (sub->getCastKind() == CK_ArrayToPointerDecay && sub->getType() == expr->getType())
582 //TODO: instead of exact QualType match, allow some variation?
584 if (loplugin::TypeCheck(sub->getType()).Pointer().Const().Char()) {
585 if (auto const lit = dyn_cast<clang::StringLiteral>(expr->getSubExprAsWritten())) {
586 if (lit->getKind() == clang::StringLiteral::UTF8) {
587 // Don't warn about
589 // redundant_cast<char const *>(u8"...")
591 // in pre-C++2a code:
592 return true;
596 report(
597 DiagnosticsEngine::Warning, "redundant reinterpret_cast from %0 to %1",
598 expr->getExprLoc())
599 << expr->getSubExprAsWritten()->getType() << expr->getTypeAsWritten()
600 << expr->getSourceRange();
601 return true;
604 if (expr->getSubExpr()->getType()->isVoidPointerType()) {
605 auto t = expr->getType()->getAs<clang::PointerType>();
606 if (t == nullptr || !t->getPointeeType()->isObjectType()) {
607 return true;
609 if (rewriter != nullptr) {
610 auto loc = compat::getBeginLoc(expr);
611 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
612 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(
613 loc);
615 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
616 auto loc2 = compat::getEndLoc(expr);
617 while (compiler.getSourceManager().isMacroArgExpansion(loc2)) {
618 loc2 = compiler.getSourceManager()
619 .getImmediateMacroCallerLoc(loc2);
621 if (compiler.getSourceManager().isMacroBodyExpansion(loc2)) {
622 //TODO: check loc, loc2 are in same macro body expansion
623 loc = compiler.getSourceManager().getSpellingLoc(loc);
626 auto s = compiler.getSourceManager().getCharacterData(loc);
627 auto n = Lexer::MeasureTokenLength(
628 loc, compiler.getSourceManager(), compiler.getLangOpts());
629 std::string tok(s, n);
630 if (tok == "reinterpret_cast" && replaceText(loc, n, "static_cast"))
632 return true;
635 report(
636 DiagnosticsEngine::Warning,
637 "reinterpret_cast from %0 to %1 can be simplified to static_cast",
638 expr->getExprLoc())
639 << expr->getSubExprAsWritten()->getType() << expr->getType()
640 << expr->getSourceRange();
641 } else if (expr->getType()->isVoidPointerType()) {
642 auto t = expr->getSubExpr()->getType()->getAs<clang::PointerType>();
643 if (t == nullptr || !t->getPointeeType()->isObjectType()) {
644 return true;
646 report(
647 DiagnosticsEngine::Warning,
648 ("reinterpret_cast from %0 to %1 can be simplified to static_cast"
649 " or an implicit conversion"),
650 expr->getExprLoc())
651 << expr->getSubExprAsWritten()->getType() << expr->getType()
652 << expr->getSourceRange();
653 } else if (expr->getType()->isFundamentalType()) {
654 if (auto const sub = dyn_cast<CXXConstCastExpr>(
655 expr->getSubExpr()->IgnoreParens()))
657 report(
658 DiagnosticsEngine::Warning,
659 ("redundant const_cast from %0 to %1 within reinterpret_cast to"
660 " fundamental type %2"),
661 expr->getExprLoc())
662 << sub->getSubExprAsWritten()->getType()
663 << sub->getTypeAsWritten() << expr->getTypeAsWritten()
664 << expr->getSourceRange();
665 return true;
668 return true;
671 bool RedundantCast::VisitCXXConstCastExpr(CXXConstCastExpr const * expr) {
672 if (ignoreLocation(expr)) {
673 return true;
675 auto const sub = compat::getSubExprAsWritten(expr);
676 if (isRedundantConstCast(expr)) {
677 report(
678 DiagnosticsEngine::Warning,
679 "redundant const_cast from %0 %1 to %2 %3", expr->getExprLoc())
680 << sub->getType() << printExprValueKind(sub->getValueKind())
681 << expr->getTypeAsWritten()
682 << printExprValueKind(expr->getValueKind())
683 << expr->getSourceRange();
684 return true;
686 if (auto const dce = dyn_cast<CXXStaticCastExpr>(
687 sub->IgnoreParenImpCasts()))
689 auto const sub2 = compat::getSubExprAsWritten(dce);
690 auto t1 = sub2->getType().getCanonicalType();
691 auto isNullptr = t1->isNullPtrType();
692 auto t2 = dce->getType().getCanonicalType();
693 auto t3 = expr->getType().getCanonicalType();
694 auto redundant = false;
695 for (;;) {
696 if ((t2.isConstQualified()
697 && (isNullptr || !t1.isConstQualified())
698 && !t3.isConstQualified())
699 || (t2.isVolatileQualified()
700 && (isNullptr || !t1.isVolatileQualified())
701 && !t3.isVolatileQualified()))
703 redundant = true;
704 break;
706 if (!isNullptr) {
707 auto const p1 = t1->getAs<clang::PointerType>();
708 if (p1 == nullptr) {
709 break;
711 t1 = p1->getPointeeType();
712 isNullptr = t1->isNullPtrType();
714 auto const p2 = t2->getAs<clang::PointerType>();
715 if (p2 == nullptr) {
716 break;
718 t2 = p2->getPointeeType();
719 auto const p3 = t3->getAs<clang::PointerType>();
720 if (p3 == nullptr) {
721 break;
723 t3 = p3->getPointeeType();
725 if (redundant) {
726 report(
727 DiagnosticsEngine::Warning,
728 ("redundant static_cast/const_cast combination from %0 via %1"
729 " to %2"),
730 expr->getExprLoc())
731 << sub2->getType() << dce->getTypeAsWritten()
732 << expr->getTypeAsWritten() << expr->getSourceRange();
735 return true;
738 bool RedundantCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr) {
739 if (ignoreLocation(expr)) {
740 return true;
742 // Restrict this to "real" casts (compared to uses of braced-init-list, like
744 // Foo{bar, baz}
746 // or
748 // std::initializer_list<Foo>{bar, baz}
750 // ), and only to cases where the sub-expression already is a prvalue of
751 // non-class type (and thus the cast is unlikely to be meant to create a
752 // temporary):
753 auto const t1 = expr->getTypeAsWritten();
754 bool const fnptr = t1->isFunctionPointerType() || t1->isMemberFunctionPointerType();
755 auto const sub = fnptr ? stopAtFunctionPointerDecay(expr) : compat::getSubExprAsWritten(expr);
756 if ((sub->getValueKind() != compat::VK_PRValue && !fnptr) || expr->getType()->isRecordType()
757 || isa<InitListExpr>(sub) || isa<CXXStdInitializerListExpr>(sub))
759 return true;
762 // See "There might even be good reasons(?) not to warn inside explicit
763 // casts" block in compilerplugins/clang/test/cppunitassertequals.cxx:
764 auto const eloc = expr->getExprLoc();
765 if (compiler.getSourceManager().isMacroArgExpansion(eloc)) {
766 auto const name = Lexer::getImmediateMacroName(
767 eloc, compiler.getSourceManager(), compiler.getLangOpts());
768 if (name == "CPPUNIT_ASSERT" || name == "CPPUNIT_ASSERT_MESSAGE") {
769 return true;
773 // Don't warn if a functional cast on a pointer to function or member function is used to
774 // disambiguate an overloaded function:
775 if (fnptr) {
776 auto e = sub->IgnoreParenImpCasts();
777 if (auto const e1 = dyn_cast<UnaryOperator>(e)) {
778 if (e1->getOpcode() == UO_AddrOf) {
779 e = e1->getSubExpr()->IgnoreParenImpCasts();
782 if (auto const e1 = dyn_cast<DeclRefExpr>(e)) {
783 if (auto const fdecl = dyn_cast<FunctionDecl>(e1->getDecl())) {
784 if (isOverloadedFunction(fdecl)) {
785 return true;
791 // See the commit message of d0e7d020fa405ab94f19916ec96fbd4611da0031
792 // "socket.c -> socket.cxx" for the reason to have
794 // bool(FD_ISSET(...))
796 // in sal/osl/unx/socket.cxx:
797 //TODO: Better check that sub is exactly an expansion of FD_ISSET:
798 if (compat::getEndLoc(sub).isMacroID()) {
799 for (auto loc = compat::getBeginLoc(sub);
800 loc.isMacroID()
801 && (compiler.getSourceManager()
802 .isAtStartOfImmediateMacroExpansion(loc));
803 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc))
805 if (Lexer::getImmediateMacroName(
806 loc, compiler.getSourceManager(), compiler.getLangOpts())
807 == "FD_ISSET")
809 return true;
814 auto const t2 = sub->getType();
815 if (t1.getCanonicalType() != t2.getCanonicalType())
816 return true;
817 if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
818 return true;
819 report(
820 DiagnosticsEngine::Warning,
821 "redundant functional cast from %0 to %1", expr->getExprLoc())
822 << t2 << t1 << expr->getSourceRange();
823 return true;
826 bool RedundantCast::VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr) {
827 if (ignoreLocation(expr)) {
828 return true;
830 auto const sub = compat::getSubExprAsWritten(expr);
831 auto const t1 = expr->getTypeAsWritten();
832 auto const t2 = sub->getType();
833 QualType qt1 = t1.getCanonicalType();
834 QualType qt2 = t2.getCanonicalType();
835 if (qt1 == qt2)
837 report(
838 DiagnosticsEngine::Warning,
839 "redundant dynamic cast from %0 to %1", expr->getExprLoc())
840 << t2 << t1 << expr->getSourceRange();
841 return true;
843 if (qt1->isPointerType() && qt2->isPointerType())
845 // casting from 'T*' to 'const T*' is redundant, so compare without the qualifiers
846 qt1 = qt1->getPointeeType().getUnqualifiedType();
847 qt2 = qt2->getPointeeType().getUnqualifiedType();
848 if (qt1 == qt2)
850 report(
851 DiagnosticsEngine::Warning,
852 "redundant dynamic cast from %0 to %1", expr->getExprLoc())
853 << t2 << t1 << expr->getSourceRange();
854 return true;
856 if (qt1->getAsCXXRecordDecl() && qt2->getAsCXXRecordDecl()->isDerivedFrom(qt1->getAsCXXRecordDecl()))
858 report(
859 DiagnosticsEngine::Warning,
860 "redundant dynamic upcast from %0 to %1", expr->getExprLoc())
861 << t2 << t1 << expr->getSourceRange();
862 return true;
865 return true;
868 bool RedundantCast::VisitCallExpr(CallExpr const * expr) {
869 if (ignoreLocation(expr)) {
870 return true;
872 auto f = expr->getDirectCallee();
873 if (f == nullptr || !f->isVariadic()
874 || expr->getNumArgs() <= f->getNumParams())
876 return true;
878 for (auto i = f->getNumParams(); i != expr->getNumArgs(); ++i) {
879 auto a = expr->getArg(i);
880 if (a->getType()->isPointerType()) {
881 auto e = dyn_cast<CXXConstCastExpr>(a->IgnoreParenImpCasts());
882 if (e != nullptr) {
883 report(
884 DiagnosticsEngine::Warning,
885 "redundant const_cast of variadic function argument",
886 e->getExprLoc())
887 << expr->getSourceRange();
891 return true;
894 bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr const * expr) {
895 if (ignoreLocation(expr)) {
896 return true;
898 auto e = dyn_cast<CXXConstCastExpr>(
899 expr->getArgument()->IgnoreParenImpCasts());
900 if (e != nullptr) {
901 report(
902 DiagnosticsEngine::Warning,
903 "redundant const_cast in delete expression", e->getExprLoc())
904 << expr->getSourceRange();
906 return true;
909 bool RedundantCast::visitBinOp(BinaryOperator const * expr) {
910 if (ignoreLocation(expr)) {
911 return true;
913 if (expr->getLHS()->getType()->isPointerType()
914 && expr->getRHS()->getType()->isPointerType())
916 auto e = dyn_cast<CXXConstCastExpr>(
917 expr->getLHS()->IgnoreParenImpCasts());
918 if (e != nullptr) {
919 report(
920 DiagnosticsEngine::Warning,
921 "redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
922 e->getExprLoc())
923 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
925 e = dyn_cast<CXXConstCastExpr>(
926 expr->getRHS()->IgnoreParenImpCasts());
927 if (e != nullptr) {
928 report(
929 DiagnosticsEngine::Warning,
930 "redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
931 e->getExprLoc())
932 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
935 return true;
938 bool RedundantCast::isOverloadedFunction(FunctionDecl const * decl) {
939 auto const ctx = decl->getDeclContext();
940 if (!ctx->isLookupContext()) {
941 return false;
943 auto const canon = decl->getCanonicalDecl();
944 auto const res = ctx->lookup(decl->getDeclName());
945 for (auto d = res.begin(); d != res.end(); ++d) {
946 if (auto const f = dyn_cast<FunctionDecl>(*d)) {
947 if (f->getCanonicalDecl() != canon) {
948 return true;
952 return false;
955 loplugin::Plugin::Registration<RedundantCast> X("redundantcast", true);
959 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */