1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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  */
9 
10 // Warn about certain redundant casts:
11 //
12 // * A reinterpret_cast<T*>(...) whose result is then implicitly cast to a void
13 //   pointer
14 //
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
17 //
18 // * Various const_casts that are either not needed (like casting away constness
19 //   in a delete expression) or are implicitly cast back afterwards
20 //
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.
25 
26 #include "clang/Sema/Sema.h"
27 
28 #include "check.hxx"
29 #include "compat.hxx"
30 #include "plugin.hxx"
31 #include <iostream>
32 
33 namespace {
34 
isVoidPointer(QualType type)35 bool isVoidPointer(QualType type) {
36     return type->isPointerType()
37         && type->getAs<clang::PointerType>()->getPointeeType()->isVoidType();
38 }
39 
isRedundantConstCast(CXXConstCastExpr const * expr)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);
47 }
48 
canConstCastFromTo(Expr const * from,Expr const * to)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()));
55 }
56 
printExprValueKind(ExprValueKind k)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";
65     };
66     llvm_unreachable("unknown ExprValueKind");
67 }
68 
69 enum class AlgebraicType { None, Integer, FloatingPoint };
70 
algebraicType(clang::Type const & type)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;
78     }
79 }
80 
81 // Do not look through FunctionToPointerDecay, but through e.g. NullToPointer:
stopAtFunctionPointerDecay(ExplicitCastExpr const * expr)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();
87         }
88     }
89     return e1;
90 }
91 
92 class RedundantCast:
93     public loplugin::FilteringRewritePlugin<RedundantCast>
94 {
95 public:
RedundantCast(loplugin::InstantiationData const & data)96     explicit RedundantCast(loplugin::InstantiationData const & data):
97         FilteringRewritePlugin(data)
98     {}
99 
run()100     virtual void run() override {
101         if (compiler.getLangOpts().CPlusPlus) {
102             TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
103         }
104     }
105 
TraverseInitListExpr(InitListExpr * expr,DataRecursionQueue * queue=nullptr)106     bool TraverseInitListExpr(InitListExpr * expr, DataRecursionQueue * queue = nullptr) {
107         return WalkUpFromInitListExpr(expr)
108             && TraverseSynOrSemInitListExpr(
109                 expr->isSemanticForm() ? expr : expr->getSemanticForm(), queue);
110     }
111 
112     bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
113 
114     bool VisitCXXStaticCastExpr(CXXStaticCastExpr const * expr);
115 
116     bool VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr const * expr);
117 
118     bool VisitCXXConstCastExpr(CXXConstCastExpr const * expr);
119 
120     bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr);
121 
122     bool VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr);
123 
124     bool VisitCallExpr(CallExpr const * expr);
125 
126     bool VisitCXXDeleteExpr(CXXDeleteExpr const * expr);
127 
128     bool VisitCStyleCastExpr(CStyleCastExpr const * expr);
129 
VisitBinaryOperator(BinaryOperator 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);
134         }
135         if (op == BO_Assign) {
136             if (ignoreLocation(expr)) {
137                 return true;
138             }
139             visitAssign(expr->getLHS()->getType(), expr->getRHS());
140             return true;
141         }
142         return true;
143     }
144 
145     bool VisitVarDecl(VarDecl const * varDecl);
146 
147 private:
148     bool visitBinOp(BinaryOperator const * expr);
149     void visitAssign(QualType lhs, Expr const * rhs);
150     bool isOverloadedFunction(FunctionDecl const * decl);
151 
isInIgnoredMacroBody(Expr const * expr)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));
156     }
157 };
158 
VisitImplicitCastExpr(const ImplicitCastExpr * expr)159 bool RedundantCast::VisitImplicitCastExpr(const ImplicitCastExpr * expr) {
160     if (ignoreLocation(expr)) {
161         return true;
162     }
163     switch (expr->getCastKind()) {
164     case CK_NoOp:
165         if (expr->getType()->isPointerType()
166             || expr->getType()->isObjectType())
167         {
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())))
179                 {
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();
187                 }
188             }
189         }
190         break;
191     case CK_BitCast:
192         if (isVoidPointer(expr->getType())
193             && expr->getSubExpr()->getType()->isPointerType())
194         {
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()))
202                 {
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();
210                 }
211                 e = cc->getSubExpr()->IgnoreParenImpCasts();
212             }
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)))
226             {
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();
233             }
234         }
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()))
246                 {
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();
254                 }
255                 e = cc->getSubExpr()->IgnoreParenImpCasts();
256             }
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()))
265                 {
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();
273                 }
274                 e = cc->getSubExpr()->IgnoreParenImpCasts();
275             }
276         }
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())))
284             {
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();
292             }
293         }
294         break;
295     default:
296         break;
297     }
298     return true;
299 }
300 
VisitCStyleCastExpr(CStyleCastExpr const * expr)301 bool RedundantCast::VisitCStyleCastExpr(CStyleCastExpr const * expr) {
302     if (ignoreLocation(expr)) {
303         return true;
304     }
305     if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr)))) {
306         return true;
307     }
308     auto t1 = compat::getSubExprAsWritten(expr)->getType();
309     auto t2 = expr->getTypeAsWritten();
310     if (auto templateType = dyn_cast<SubstTemplateTypeParmType>(t1)) {
311         t1 = templateType->desugar();
312     }
313     if (t1 != t2) {
314         return true;
315     }
316     if (!t1->isBuiltinType() && !loplugin::TypeCheck(t1).Enum() && !loplugin::TypeCheck(t1).Typedef()) {
317         return true;
318     }
319     if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
320     {
321         return true;
322     }
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);
329     }
330     auto l2 = expr->getExprLoc();
331     while (compiler.getSourceManager().isMacroArgExpansion(l2)) {
332         l2 = compiler.getSourceManager().getImmediateMacroCallerLoc(l2);
333     }
334     auto l3 = compat::getEndLoc(expr);
335     while (compiler.getSourceManager().isMacroArgExpansion(l3)) {
336          l3 = compiler.getSourceManager().getImmediateMacroCallerLoc(l3);
337     }
338     if (compiler.getSourceManager().isMacroBodyExpansion(l1)
339         && compiler.getSourceManager().isMacroBodyExpansion(l2)
340         && compiler.getSourceManager().isMacroBodyExpansion(l3)
341         && ignoreLocation(compiler.getSourceManager().getSpellingLoc(l2)))
342     {
343         return true;
344     }
345     report(
346         DiagnosticsEngine::Warning,
347         "redundant cstyle cast from %0 to %1", expr->getExprLoc())
348         << t1 << t2 << expr->getSourceRange();
349     return true;
350 }
351 
VisitVarDecl(VarDecl const * varDecl)352 bool RedundantCast::VisitVarDecl(VarDecl const * varDecl) {
353     if (ignoreLocation(varDecl)) {
354         return true;
355     }
356     if (!varDecl->getInit())
357         return true;
358     visitAssign(varDecl->getType(), varDecl->getInit());
359     return true;
360 }
361 
visitAssign(QualType t1,Expr const * rhs)362 void RedundantCast::visitAssign(QualType t1, Expr const * rhs)
363 {
364     auto staticCastExpr = dyn_cast<CXXStaticCastExpr>(rhs->IgnoreImplicit());
365     if (!staticCastExpr)
366         return;
367 
368     auto const t2 = staticCastExpr->getSubExpr()->IgnoreImplicit()->getType();
369 
370     // if there is more than one copy of the LHS, this cast is resolving ambiguity
371     bool foundOne = false;
372     if (t1->isRecordType())
373     {
374         foundOne = loplugin::derivedFromCount(t2, t1) == 1;
375     }
376     else
377     {
378         auto pointee1 = t1->getPointeeCXXRecordDecl();
379         auto pointee2 = t2->getPointeeCXXRecordDecl();
380         if (pointee1 && pointee2)
381             foundOne = loplugin::derivedFromCount(pointee2, pointee1) == 1;
382     }
383 
384     if (foundOne)
385     {
386         report(
387             DiagnosticsEngine::Warning, "redundant static_cast from %0 to %1",
388             staticCastExpr->getExprLoc())
389             << t2 << t1 << staticCastExpr->getSourceRange();
390     }
391 }
392 
VisitCXXStaticCastExpr(CXXStaticCastExpr const * expr)393 bool RedundantCast::VisitCXXStaticCastExpr(CXXStaticCastExpr const * expr) {
394     if (ignoreLocation(expr)) {
395         return true;
396     }
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;
416     }
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?
420         {
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)))
431             {
432                 report(
433                     DiagnosticsEngine::Warning, "redundant static_cast from %0 to %1",
434                     expr->getExprLoc())
435                     << expr->getSubExprAsWritten()->getType() << t2 << expr->getSourceRange();
436             }
437             return true;
438         }
439     }
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)
445     {
446         bool ObjCLifetimeConversion;
447         if (nonClassObjectType
448             || (c1.getTypePtr() != c3.getTypePtr()
449                 && !compiler.getSema().IsQualificationConversion(
450                     c1, c3, false, ObjCLifetimeConversion)))
451         {
452             return true;
453         }
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;
462     }
463     if (!loplugin::isOkToRemoveArithmeticCast(compiler.getASTContext(), t1, t2, expr->getSubExpr()))
464     {
465         return true;
466     }
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;
476             }
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;
481             }
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;
489                 }
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;
494                 }
495             }
496         }
497     }
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))
502     {
503         return true;
504     }
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();
512             }
513         }
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;
518                 }
519             }
520         }
521     }
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"))
532         {
533             return true;
534         }
535     }
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 == compat::VK_PRValue && (k1 != compat::VK_PRValue || t1->isRecordType()))
543         << expr->getSourceRange();
544     return true;
545 }
546 
VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr const * expr)547 bool RedundantCast::VisitCXXReinterpretCastExpr(
548     CXXReinterpretCastExpr const * expr)
549 {
550     if (ignoreLocation(expr)) {
551         return true;
552     }
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?
556         {
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
561                         //
562                         //   redundant_cast<char const *>(u8"...")
563                         //
564                         // in pre-C++2a code:
565                         return true;
566                     }
567                 }
568             }
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;
575         }
576     }
577     if (expr->getSubExpr()->getType()->isVoidPointerType()) {
578         auto t = expr->getType()->getAs<clang::PointerType>();
579         if (t == nullptr || !t->getPointeeType()->isObjectType()) {
580             return true;
581         }
582         if (rewriter != nullptr) {
583             auto loc = compat::getBeginLoc(expr);
584             while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
585                 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(
586                     loc);
587             }
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);
593                 }
594                 if (compiler.getSourceManager().isMacroBodyExpansion(loc2)) {
595                     //TODO: check loc, loc2 are in same macro body expansion
596                     loc = compiler.getSourceManager().getSpellingLoc(loc);
597                 }
598             }
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"))
604             {
605                 return true;
606             }
607         }
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;
618         }
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()))
629         {
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;
639         }
640     }
641     return true;
642 }
643 
VisitCXXConstCastExpr(CXXConstCastExpr const * expr)644 bool RedundantCast::VisitCXXConstCastExpr(CXXConstCastExpr const * expr) {
645     if (ignoreLocation(expr)) {
646         return true;
647     }
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;
658     }
659     if (auto const dce = dyn_cast<CXXStaticCastExpr>(
660             sub->IgnoreParenImpCasts()))
661     {
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()))
675             {
676                 redundant = true;
677                 break;
678             }
679             if (!isNullptr) {
680                 auto const p1 = t1->getAs<clang::PointerType>();
681                 if (p1 == nullptr) {
682                     break;
683                 }
684                 t1 = p1->getPointeeType();
685                 isNullptr = t1->isNullPtrType();
686             }
687             auto const p2 = t2->getAs<clang::PointerType>();
688             if (p2 == nullptr) {
689                 break;
690             }
691             t2 = p2->getPointeeType();
692             auto const p3 = t3->getAs<clang::PointerType>();
693             if (p3 == nullptr) {
694                 break;
695             }
696             t3 = p3->getPointeeType();
697         }
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();
706         }
707     }
708     return true;
709 }
710 
VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr)711 bool RedundantCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr) {
712     if (ignoreLocation(expr)) {
713         return true;
714     }
715     // Restrict this to "real" casts (compared to uses of braced-init-list, like
716     //
717     //   Foo{bar, baz}
718     //
719     // or
720     //
721     //   std::initializer_list<Foo>{bar, baz}
722     //
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() != compat::VK_PRValue && !fnptr) || expr->getType()->isRecordType()
730         || isa<InitListExpr>(sub) || isa<CXXStdInitializerListExpr>(sub))
731     {
732         return true;
733     }
734 
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;
743         }
744     }
745 
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();
753             }
754         }
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;
759                 }
760             }
761         }
762     }
763 
764     // See the commit message of d0e7d020fa405ab94f19916ec96fbd4611da0031
765     // "socket.c -> socket.cxx" for the reason to have
766     //
767     //   bool(FD_ISSET(...))
768     //
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))
777         {
778             if (Lexer::getImmediateMacroName(
779                     loc, compiler.getSourceManager(), compiler.getLangOpts())
780                 == "FD_ISSET")
781             {
782                 return true;
783             }
784         }
785     }
786 
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;
797 }
798 
VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr)799 bool RedundantCast::VisitCXXDynamicCastExpr(CXXDynamicCastExpr const * expr) {
800     if (ignoreLocation(expr)) {
801         return true;
802     }
803     auto const sub = compat::getSubExprAsWritten(expr);
804     auto const t1 = expr->getTypeAsWritten();
805     auto const t2 = sub->getType();
806     QualType qt1 = t1.getCanonicalType();
807     QualType qt2 = t2.getCanonicalType();
808     if (qt1 == qt2)
809     {
810         report(
811             DiagnosticsEngine::Warning,
812             "redundant dynamic cast from %0 to %1", expr->getExprLoc())
813             << t2 << t1 << expr->getSourceRange();
814         return true;
815     }
816     if (qt1->isPointerType() && qt2->isPointerType())
817     {
818         // casting from 'T*' to 'const T*' is redundant, so compare without the qualifiers
819         qt1 = qt1->getPointeeType().getUnqualifiedType();
820         qt2 = qt2->getPointeeType().getUnqualifiedType();
821         if (qt1 == qt2)
822         {
823             report(
824                 DiagnosticsEngine::Warning,
825                 "redundant dynamic cast from %0 to %1", expr->getExprLoc())
826                 << t2 << t1 << expr->getSourceRange();
827             return true;
828         }
829         if (qt1->getAsCXXRecordDecl() && qt2->getAsCXXRecordDecl()->isDerivedFrom(qt1->getAsCXXRecordDecl()))
830         {
831             report(
832                 DiagnosticsEngine::Warning,
833                 "redundant dynamic upcast from %0 to %1", expr->getExprLoc())
834                 << t2 << t1 << expr->getSourceRange();
835             return true;
836         }
837     }
838     return true;
839 }
840 
VisitCallExpr(CallExpr const * expr)841 bool RedundantCast::VisitCallExpr(CallExpr const * expr) {
842     if (ignoreLocation(expr)) {
843         return true;
844     }
845     auto f = expr->getDirectCallee();
846     if (f == nullptr || !f->isVariadic()
847         || expr->getNumArgs() <= f->getNumParams())
848     {
849         return true;
850     }
851     for (auto i = f->getNumParams(); i != expr->getNumArgs(); ++i) {
852         auto a = expr->getArg(i);
853         if (a->getType()->isPointerType()) {
854             auto e = dyn_cast<CXXConstCastExpr>(a->IgnoreParenImpCasts());
855             if (e != nullptr) {
856                 report(
857                     DiagnosticsEngine::Warning,
858                     "redundant const_cast of variadic function argument",
859                     e->getExprLoc())
860                     << expr->getSourceRange();
861             }
862         }
863     }
864     return true;
865 }
866 
VisitCXXDeleteExpr(CXXDeleteExpr const * expr)867 bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr const * expr) {
868     if (ignoreLocation(expr)) {
869         return true;
870     }
871     auto e = dyn_cast<CXXConstCastExpr>(
872         expr->getArgument()->IgnoreParenImpCasts());
873     if (e != nullptr) {
874         report(
875             DiagnosticsEngine::Warning,
876             "redundant const_cast in delete expression", e->getExprLoc())
877             << expr->getSourceRange();
878     }
879     return true;
880 }
881 
visitBinOp(BinaryOperator const * expr)882 bool RedundantCast::visitBinOp(BinaryOperator const * expr) {
883     if (ignoreLocation(expr)) {
884         return true;
885     }
886     if (expr->getLHS()->getType()->isPointerType()
887         && expr->getRHS()->getType()->isPointerType())
888     {
889         auto e = dyn_cast<CXXConstCastExpr>(
890             expr->getLHS()->IgnoreParenImpCasts());
891         if (e != nullptr) {
892             report(
893                 DiagnosticsEngine::Warning,
894                 "redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
895                 e->getExprLoc())
896                 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
897         }
898         e = dyn_cast<CXXConstCastExpr>(
899             expr->getRHS()->IgnoreParenImpCasts());
900         if (e != nullptr) {
901             report(
902                 DiagnosticsEngine::Warning,
903                 "redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
904                 e->getExprLoc())
905                 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
906         }
907     }
908     return true;
909 }
910 
isOverloadedFunction(FunctionDecl const * decl)911 bool RedundantCast::isOverloadedFunction(FunctionDecl const * decl) {
912     auto const ctx = decl->getDeclContext();
913     if (!ctx->isLookupContext()) {
914         return false;
915     }
916     auto const canon = decl->getCanonicalDecl();
917     auto const res = ctx->lookup(decl->getDeclName());
918     for (auto d = res.begin(); d != res.end(); ++d) {
919         if (auto const f = dyn_cast<FunctionDecl>(*d)) {
920             if (f->getCanonicalDecl() != canon) {
921                 return true;
922             }
923         }
924     }
925     return false;
926 }
927 
928 loplugin::Plugin::Registration<RedundantCast> X("redundantcast", true);
929 
930 }
931 
932 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
933