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