2015-01-29 08:16:13 +01:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Warn about certain redundant casts:
|
|
|
|
//
|
|
|
|
// * A reinterpret_cast<T*>(...) whose result is then implicitly cast to a void
|
|
|
|
// pointer
|
|
|
|
//
|
|
|
|
// * A static_cast<T*>(e) where e is of void pointer type and whose result is
|
|
|
|
// then implicitly cast to a void pointer
|
|
|
|
//
|
2015-03-26 15:33:04 +01:00
|
|
|
// * Various const_casts that are either not needed (like casting away constness
|
|
|
|
// in a delete expression) or are implicitly cast back afterwards
|
|
|
|
//
|
2015-01-29 08:16:13 +01:00
|
|
|
// C-style casts are ignored because it makes this plugin simpler, and they
|
|
|
|
// should eventually be eliminated via loplugin:cstylecast and/or
|
|
|
|
// -Wold-style-cast. That implies that this plugin is only relevant for C++
|
|
|
|
// code.
|
|
|
|
|
2015-03-26 15:33:04 +01:00
|
|
|
#include "clang/Sema/Sema.h"
|
|
|
|
|
2015-03-28 18:56:43 +01:00
|
|
|
#include "compat.hxx"
|
2015-01-29 08:16:13 +01:00
|
|
|
#include "plugin.hxx"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
bool isVoidPointer(QualType type) {
|
|
|
|
return type->isPointerType()
|
2016-03-20 13:29:50 +01:00
|
|
|
&& type->getAs<clang::PointerType>()->getPointeeType()->isVoidType();
|
2015-01-29 08:16:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class RedundantCast:
|
2015-03-31 13:18:16 +02:00
|
|
|
public RecursiveASTVisitor<RedundantCast>, public loplugin::RewritePlugin
|
2015-01-29 08:16:13 +01:00
|
|
|
{
|
|
|
|
public:
|
2015-03-31 13:18:16 +02:00
|
|
|
explicit RedundantCast(InstantiationData const & data): RewritePlugin(data)
|
|
|
|
{}
|
2015-01-29 08:16:13 +01:00
|
|
|
|
|
|
|
virtual void run() override {
|
|
|
|
if (compiler.getLangOpts().CPlusPlus) {
|
|
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
|
2015-03-26 15:33:04 +01:00
|
|
|
|
2015-03-31 13:18:16 +02:00
|
|
|
bool VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr const * expr);
|
|
|
|
|
2015-05-29 12:05:09 +02:00
|
|
|
bool VisitCXXConstCastExpr(CXXConstCastExpr const * expr);
|
|
|
|
|
2015-03-26 15:33:04 +01:00
|
|
|
bool VisitCallExpr(CallExpr const * expr);
|
|
|
|
|
|
|
|
bool VisitCXXDeleteExpr(CXXDeleteExpr const * expr);
|
|
|
|
|
|
|
|
bool VisitBinSub(BinaryOperator const * expr)
|
|
|
|
{ return visitBinOp(expr); }
|
|
|
|
|
|
|
|
bool VisitBinLT(BinaryOperator const * expr)
|
|
|
|
{ return visitBinOp(expr); }
|
|
|
|
|
|
|
|
bool VisitBinGT(BinaryOperator const * expr)
|
|
|
|
{ return visitBinOp(expr); }
|
|
|
|
|
|
|
|
bool VisitBinLE(BinaryOperator const * expr)
|
|
|
|
{ return visitBinOp(expr); }
|
|
|
|
|
|
|
|
bool VisitBinGE(BinaryOperator const * expr)
|
|
|
|
{ return visitBinOp(expr); }
|
|
|
|
|
|
|
|
bool VisitBinEQ(BinaryOperator const * expr)
|
|
|
|
{ return visitBinOp(expr); }
|
|
|
|
|
|
|
|
bool VisitBinNE(BinaryOperator const * expr)
|
|
|
|
{ return visitBinOp(expr); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool visitBinOp(BinaryOperator const * expr);
|
2015-01-29 08:16:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
bool RedundantCast::VisitImplicitCastExpr(const ImplicitCastExpr * expr) {
|
2015-03-26 15:33:04 +01:00
|
|
|
if (ignoreLocation(expr)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
switch (expr->getCastKind()) {
|
|
|
|
case CK_NoOp:
|
|
|
|
if (expr->getType()->isPointerType()
|
|
|
|
|| expr->getType()->isObjectType())
|
|
|
|
{
|
|
|
|
auto e = dyn_cast<CXXConstCastExpr>(
|
|
|
|
expr->getSubExpr()->IgnoreParenImpCasts());
|
|
|
|
if (e != nullptr) {
|
|
|
|
auto t1 = e->getSubExpr()->getType().getCanonicalType();
|
|
|
|
auto t2 = expr->getType().getCanonicalType();
|
|
|
|
bool ObjCLifetimeConversion;
|
|
|
|
if (t1.getTypePtr() == t2.getTypePtr()
|
|
|
|
|| compiler.getSema().IsQualificationConversion(
|
|
|
|
t1, t2, false, ObjCLifetimeConversion))
|
|
|
|
{
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
("redundant const_cast from %0 to %1, result is"
|
|
|
|
" implictly cast to %2"),
|
|
|
|
e->getExprLoc())
|
|
|
|
<< e->getSubExprAsWritten()->getType() << e->getType()
|
|
|
|
<< expr->getType() << expr->getSourceRange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CK_BitCast:
|
|
|
|
if (isVoidPointer(expr->getType())
|
|
|
|
&& expr->getSubExpr()->getType()->isPointerType())
|
|
|
|
{
|
|
|
|
Expr const * e = expr->getSubExpr()->IgnoreParenImpCasts();
|
|
|
|
while (isa<CXXConstCastExpr>(e)) {
|
|
|
|
auto cc = dyn_cast<CXXConstCastExpr>(e);
|
2016-03-20 13:29:50 +01:00
|
|
|
if (expr->getType()->getAs<clang::PointerType>()
|
|
|
|
->getPointeeType().isAtLeastAsQualifiedAs(
|
2015-03-26 15:33:04 +01:00
|
|
|
cc->getSubExpr()->getType()
|
2016-03-20 13:29:50 +01:00
|
|
|
->getAs<clang::PointerType>()->getPointeeType()))
|
2015-03-26 15:33:04 +01:00
|
|
|
{
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
("redundant const_cast from %0 to %1, result is"
|
|
|
|
" ultimately implictly cast to %2"),
|
|
|
|
cc->getExprLoc())
|
|
|
|
<< cc->getSubExprAsWritten()->getType() << cc->getType()
|
|
|
|
<< expr->getType() << expr->getSourceRange();
|
|
|
|
}
|
|
|
|
e = cc->getSubExpr()->IgnoreParenImpCasts();
|
|
|
|
}
|
|
|
|
if (isa<CXXReinterpretCastExpr>(e)) {
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
("redundant reinterpret_cast, result is implicitly cast to"
|
|
|
|
" void pointer"),
|
|
|
|
e->getExprLoc())
|
|
|
|
<< e->getSourceRange();
|
|
|
|
} else if (isa<CXXStaticCastExpr>(e)
|
|
|
|
&& isVoidPointer(
|
|
|
|
dyn_cast<CXXStaticCastExpr>(e)->getSubExpr()
|
2015-03-28 18:56:43 +01:00
|
|
|
->IgnoreParenImpCasts()->getType())
|
|
|
|
&& !compat::isMacroBodyExpansion(
|
|
|
|
compiler, e->getLocStart()))
|
2015-03-26 15:33:04 +01:00
|
|
|
{
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
("redundant static_cast from void pointer, result is"
|
|
|
|
" implicitly cast to void pointer"),
|
|
|
|
e->getExprLoc())
|
|
|
|
<< e->getSourceRange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2015-04-13 12:35:57 +02:00
|
|
|
case CK_DerivedToBase:
|
|
|
|
case CK_UncheckedDerivedToBase:
|
|
|
|
if (expr->getType()->isPointerType()) {
|
|
|
|
Expr const * e = expr->getSubExpr()->IgnoreParenImpCasts();
|
|
|
|
while (isa<CXXConstCastExpr>(e)) {
|
|
|
|
auto cc = dyn_cast<CXXConstCastExpr>(e);
|
2016-03-20 13:29:50 +01:00
|
|
|
if (expr->getType()->getAs<clang::PointerType>()
|
|
|
|
->getPointeeType().isAtLeastAsQualifiedAs(
|
2015-04-13 12:35:57 +02:00
|
|
|
cc->getSubExpr()->getType()
|
2016-03-20 13:29:50 +01:00
|
|
|
->getAs<clang::PointerType>()->getPointeeType()))
|
2015-04-13 12:35:57 +02:00
|
|
|
{
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
("redundant const_cast from %0 to %1, result is"
|
|
|
|
" ultimately implictly cast to %2"),
|
|
|
|
cc->getExprLoc())
|
|
|
|
<< cc->getSubExprAsWritten()->getType() << cc->getType()
|
|
|
|
<< expr->getType() << expr->getSourceRange();
|
|
|
|
}
|
|
|
|
e = cc->getSubExpr()->IgnoreParenImpCasts();
|
|
|
|
}
|
|
|
|
} else if (expr->getType()->isReferenceType()) {
|
|
|
|
Expr const * e = expr->getSubExpr()->IgnoreParenImpCasts();
|
|
|
|
while (isa<CXXConstCastExpr>(e)) {
|
|
|
|
auto cc = dyn_cast<CXXConstCastExpr>(e);
|
|
|
|
if (expr->getType()->getAs<ReferenceType>()->getPointeeType()
|
|
|
|
.isAtLeastAsQualifiedAs(
|
|
|
|
cc->getSubExpr()->getType()
|
|
|
|
->getAs<ReferenceType>()->getPointeeType()))
|
|
|
|
{
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
("redundant const_cast from %0 to %1, result is"
|
|
|
|
" ultimately implictly cast to %2"),
|
|
|
|
cc->getExprLoc())
|
|
|
|
<< cc->getSubExprAsWritten()->getType() << cc->getType()
|
|
|
|
<< expr->getType() << expr->getSourceRange();
|
|
|
|
}
|
|
|
|
e = cc->getSubExpr()->IgnoreParenImpCasts();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2015-03-26 15:33:04 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-31 13:18:16 +02:00
|
|
|
bool RedundantCast::VisitCXXReinterpretCastExpr(
|
|
|
|
CXXReinterpretCastExpr const * expr)
|
|
|
|
{
|
2015-05-12 18:27:37 +02:00
|
|
|
if (ignoreLocation(expr)) {
|
2015-03-31 13:18:16 +02:00
|
|
|
return true;
|
|
|
|
}
|
2015-05-12 18:27:37 +02:00
|
|
|
if (expr->getSubExpr()->getType()->isVoidPointerType()) {
|
2016-03-20 13:29:50 +01:00
|
|
|
auto t = expr->getType()->getAs<clang::PointerType>();
|
2015-05-12 18:27:37 +02:00
|
|
|
if (t == nullptr || !t->getPointeeType()->isObjectType()) {
|
|
|
|
return true;
|
2015-03-31 13:18:16 +02:00
|
|
|
}
|
2015-05-12 18:27:37 +02:00
|
|
|
if (rewriter != nullptr) {
|
|
|
|
auto loc = expr->getLocStart();
|
|
|
|
while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
|
|
|
|
loc = compiler.getSourceManager().getImmediateMacroCallerLoc(
|
|
|
|
loc);
|
2015-03-31 13:18:16 +02:00
|
|
|
}
|
2015-05-12 18:27:37 +02:00
|
|
|
if (compat::isMacroBodyExpansion(compiler, loc)) {
|
|
|
|
auto loc2 = expr->getLocEnd();
|
|
|
|
while (compiler.getSourceManager().isMacroArgExpansion(loc2)) {
|
|
|
|
loc2 = compiler.getSourceManager()
|
|
|
|
.getImmediateMacroCallerLoc(loc2);
|
|
|
|
}
|
|
|
|
if (compat::isMacroBodyExpansion(compiler, loc2)) {
|
|
|
|
//TODO: check loc, loc2 are in same macro body expansion
|
|
|
|
loc = compiler.getSourceManager().getSpellingLoc(loc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto s = compiler.getSourceManager().getCharacterData(loc);
|
|
|
|
auto n = Lexer::MeasureTokenLength(
|
|
|
|
loc, compiler.getSourceManager(), compiler.getLangOpts());
|
|
|
|
std::string tok(s, n);
|
|
|
|
if (tok == "reinterpret_cast" && replaceText(loc, n, "static_cast"))
|
|
|
|
{
|
|
|
|
return true;
|
2015-03-31 13:18:16 +02:00
|
|
|
}
|
|
|
|
}
|
2015-05-12 18:27:37 +02:00
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
"reinterpret_cast from %0 to %1 can be simplified to static_cast",
|
|
|
|
expr->getExprLoc())
|
|
|
|
<< expr->getSubExprAsWritten()->getType() << expr->getType()
|
|
|
|
<< expr->getSourceRange();
|
|
|
|
} else if (expr->getType()->isVoidPointerType()) {
|
2016-03-20 13:29:50 +01:00
|
|
|
auto t = expr->getSubExpr()->getType()->getAs<clang::PointerType>();
|
2015-05-12 18:27:37 +02:00
|
|
|
if (t == nullptr || !t->getPointeeType()->isObjectType()) {
|
2015-03-31 13:18:16 +02:00
|
|
|
return true;
|
|
|
|
}
|
2015-05-12 18:27:37 +02:00
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
("reinterpret_cast from %0 to %1 can be simplified to static_cast"
|
|
|
|
" or an implicit conversion"),
|
|
|
|
expr->getExprLoc())
|
|
|
|
<< expr->getSubExprAsWritten()->getType() << expr->getType()
|
|
|
|
<< expr->getSourceRange();
|
2015-03-31 13:18:16 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-05-29 12:05:09 +02:00
|
|
|
bool RedundantCast::VisitCXXConstCastExpr(CXXConstCastExpr const * expr) {
|
|
|
|
if (ignoreLocation(expr)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (expr->getTypeAsWritten().getCanonicalType().getTypePtr()
|
|
|
|
== (expr->getSubExprAsWritten()->getType().getCanonicalType()
|
|
|
|
.getTypePtr()))
|
|
|
|
{
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning, "redundant const_cast from %0 to %1",
|
|
|
|
expr->getExprLoc())
|
|
|
|
<< expr->getSubExprAsWritten()->getType()
|
|
|
|
<< expr->getTypeAsWritten() << expr->getSourceRange();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-26 15:33:04 +01:00
|
|
|
bool RedundantCast::VisitCallExpr(CallExpr const * expr) {
|
|
|
|
if (ignoreLocation(expr)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
auto f = expr->getDirectCallee();
|
|
|
|
if (f == nullptr || !f->isVariadic()
|
|
|
|
|| expr->getNumArgs() <= f->getNumParams())
|
2015-01-29 08:16:13 +01:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2015-03-26 15:33:04 +01:00
|
|
|
for (auto i = f->getNumParams(); i != expr->getNumArgs(); ++i) {
|
|
|
|
auto a = expr->getArg(i);
|
|
|
|
if (a->getType()->isPointerType()) {
|
|
|
|
auto e = dyn_cast<CXXConstCastExpr>(a->IgnoreParenImpCasts());
|
|
|
|
if (e != nullptr) {
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
"redundant const_cast of variadic function argument",
|
|
|
|
e->getExprLoc())
|
|
|
|
<< expr->getSourceRange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr const * expr) {
|
|
|
|
if (ignoreLocation(expr)) {
|
|
|
|
return true;
|
2015-01-29 08:16:13 +01:00
|
|
|
}
|
2015-03-26 15:33:04 +01:00
|
|
|
auto e = dyn_cast<CXXConstCastExpr>(
|
|
|
|
expr->getArgument()->IgnoreParenImpCasts());
|
|
|
|
if (e != nullptr) {
|
2015-01-29 08:16:13 +01:00
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
2015-03-26 15:33:04 +01:00
|
|
|
"redundant const_cast in delete expression", e->getExprLoc())
|
|
|
|
<< expr->getSourceRange();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RedundantCast::visitBinOp(BinaryOperator const * expr) {
|
|
|
|
if (ignoreLocation(expr)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (expr->getLHS()->getType()->isPointerType()
|
|
|
|
&& expr->getRHS()->getType()->isPointerType())
|
2015-01-29 08:16:13 +01:00
|
|
|
{
|
2015-03-26 15:33:04 +01:00
|
|
|
auto e = dyn_cast<CXXConstCastExpr>(
|
|
|
|
expr->getLHS()->IgnoreParenImpCasts());
|
|
|
|
if (e != nullptr) {
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
"redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
|
|
|
|
e->getExprLoc())
|
|
|
|
<< (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
|
|
|
|
}
|
|
|
|
e = dyn_cast<CXXConstCastExpr>(
|
|
|
|
expr->getRHS()->IgnoreParenImpCasts());
|
|
|
|
if (e != nullptr) {
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
"redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
|
|
|
|
e->getExprLoc())
|
|
|
|
<< (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
|
|
|
|
}
|
2015-01-29 08:16:13 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-31 13:18:16 +02:00
|
|
|
loplugin::Plugin::Registration<RedundantCast> X("redundantcast", true);
|
2015-01-29 08:16:13 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|