loplugin:redundantcast: redundant static_casts
Change-Id: I4d50b77745d68a23136221ef06f327137e89fa7e
This commit is contained in:
@@ -67,6 +67,15 @@ TypeCheck TypeCheck::Pointer() const {
|
||||
return TypeCheck();
|
||||
}
|
||||
|
||||
TypeCheck TypeCheck::Typedef() const {
|
||||
if (!type_.isNull()) {
|
||||
if (auto const t = type_->getAs<clang::TypedefType>()) {
|
||||
return TypeCheck(t->desugar());
|
||||
}
|
||||
}
|
||||
return TypeCheck();
|
||||
}
|
||||
|
||||
TypeCheck TypeCheck::NotSubstTemplateTypeParmType() const {
|
||||
return
|
||||
(!type_.isNull()
|
||||
|
@@ -48,6 +48,11 @@ public:
|
||||
template<std::size_t N> inline ContextCheck Class(char const (& id)[N])
|
||||
const;
|
||||
|
||||
TypeCheck Typedef() const;
|
||||
|
||||
template<std::size_t N> inline ContextCheck Typedef(char const (& id)[N])
|
||||
const;
|
||||
|
||||
TypeCheck NotSubstTemplateTypeParmType() const;
|
||||
|
||||
private:
|
||||
@@ -152,6 +157,22 @@ template<std::size_t N> ContextCheck TypeCheck::Class(char const (& id)[N])
|
||||
return ContextCheck();
|
||||
}
|
||||
|
||||
template<std::size_t N> ContextCheck TypeCheck::Typedef(char const (& id)[N])
|
||||
const
|
||||
{
|
||||
if (!type_.isNull()) {
|
||||
if (auto const t = type_->getAs<clang::TypedefType>()) {
|
||||
auto const d = t->getDecl();
|
||||
auto const i = d->getIdentifier();
|
||||
assert(i != nullptr);
|
||||
if (i->isStr(id)) {
|
||||
return ContextCheck(d->getDeclContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
return ContextCheck();
|
||||
}
|
||||
|
||||
template<std::size_t N> ContextCheck DeclCheck::Class(char const (& id)[N])
|
||||
const
|
||||
{
|
||||
|
@@ -25,10 +25,83 @@
|
||||
|
||||
#include "clang/Sema/Sema.h"
|
||||
|
||||
#include "check.hxx"
|
||||
#include "plugin.hxx"
|
||||
|
||||
namespace {
|
||||
|
||||
// Work around <http://reviews.llvm.org/D22128>:
|
||||
//
|
||||
// SfxErrorHandler::GetClassString (svtools/source/misc/ehdl.cxx):
|
||||
//
|
||||
// ErrorResource_Impl aEr(aId, (sal_uInt16)lClassId);
|
||||
// if(aEr)
|
||||
// {
|
||||
// rStr = static_cast<ResString>(aEr).GetString();
|
||||
// }
|
||||
//
|
||||
// expr->dump():
|
||||
// CXXStaticCastExpr 0x2b74e8e657b8 'class ResString' static_cast<class ResString> <ConstructorConversion>
|
||||
// `-CXXBindTemporaryExpr 0x2b74e8e65798 'class ResString' (CXXTemporary 0x2b74e8e65790)
|
||||
// `-CXXConstructExpr 0x2b74e8e65758 'class ResString' 'void (class ResString &&) noexcept(false)' elidable
|
||||
// `-MaterializeTemporaryExpr 0x2b74e8e65740 'class ResString' xvalue
|
||||
// `-CXXBindTemporaryExpr 0x2b74e8e65720 'class ResString' (CXXTemporary 0x2b74e8e65718)
|
||||
// `-ImplicitCastExpr 0x2b74e8e65700 'class ResString' <UserDefinedConversion>
|
||||
// `-CXXMemberCallExpr 0x2b74e8e656d8 'class ResString'
|
||||
// `-MemberExpr 0x2b74e8e656a0 '<bound member function type>' .operator ResString 0x2b74e8dc1f00
|
||||
// `-DeclRefExpr 0x2b74e8e65648 'struct ErrorResource_Impl' lvalue Var 0x2b74e8e653b0 'aEr' 'struct ErrorResource_Impl'
|
||||
// expr->getSubExprAsWritten()->dump():
|
||||
// MaterializeTemporaryExpr 0x2b74e8e65740 'class ResString' xvalue
|
||||
// `-CXXBindTemporaryExpr 0x2b74e8e65720 'class ResString' (CXXTemporary 0x2b74e8e65718)
|
||||
// `-ImplicitCastExpr 0x2b74e8e65700 'class ResString' <UserDefinedConversion>
|
||||
// `-CXXMemberCallExpr 0x2b74e8e656d8 'class ResString'
|
||||
// `-MemberExpr 0x2b74e8e656a0 '<bound member function type>' .operator ResString 0x2b74e8dc1f00
|
||||
// `-DeclRefExpr 0x2b74e8e65648 'struct ErrorResource_Impl' lvalue Var 0x2b74e8e653b0 'aEr' 'struct ErrorResource_Impl'
|
||||
//
|
||||
// Copies code from Clang's lib/AST/Expr.cpp:
|
||||
namespace {
|
||||
Expr *skipImplicitTemporary(Expr *expr) {
|
||||
// Skip through reference binding to temporary.
|
||||
if (MaterializeTemporaryExpr *Materialize
|
||||
= dyn_cast<MaterializeTemporaryExpr>(expr))
|
||||
expr = Materialize->GetTemporaryExpr();
|
||||
|
||||
// Skip any temporary bindings; they're implicit.
|
||||
if (CXXBindTemporaryExpr *Binder = dyn_cast<CXXBindTemporaryExpr>(expr))
|
||||
expr = Binder->getSubExpr();
|
||||
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
Expr *getSubExprAsWritten(CastExpr *This) {
|
||||
Expr *SubExpr = nullptr;
|
||||
CastExpr *E = This;
|
||||
do {
|
||||
SubExpr = skipImplicitTemporary(E->getSubExpr());
|
||||
|
||||
// Conversions by constructor and conversion functions have a
|
||||
// subexpression describing the call; strip it off.
|
||||
if (E->getCastKind() == CK_ConstructorConversion)
|
||||
SubExpr =
|
||||
skipImplicitTemporary(cast<CXXConstructExpr>(SubExpr)->getArg(0));
|
||||
else if (E->getCastKind() == CK_UserDefinedConversion) {
|
||||
assert((isa<CXXMemberCallExpr>(SubExpr) ||
|
||||
isa<BlockExpr>(SubExpr)) &&
|
||||
"Unexpected SubExpr for CK_UserDefinedConversion.");
|
||||
if (isa<CXXMemberCallExpr>(SubExpr))
|
||||
SubExpr = cast<CXXMemberCallExpr>(SubExpr)->getImplicitObjectArgument();
|
||||
}
|
||||
|
||||
// If the subexpression we're left with is an implicit cast, look
|
||||
// through that, too.
|
||||
} while ((E = dyn_cast<ImplicitCastExpr>(SubExpr)));
|
||||
|
||||
return SubExpr;
|
||||
}
|
||||
const Expr *getSubExprAsWritten(const CastExpr *This) {
|
||||
return getSubExprAsWritten(const_cast<CastExpr *>(This));
|
||||
}
|
||||
|
||||
bool isVoidPointer(QualType type) {
|
||||
return type->isPointerType()
|
||||
&& type->getAs<clang::PointerType>()->getPointeeType()->isVoidType();
|
||||
@@ -49,6 +122,8 @@ public:
|
||||
|
||||
bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
|
||||
|
||||
bool VisitCXXStaticCastExpr(CXXStaticCastExpr const * expr);
|
||||
|
||||
bool VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr const * expr);
|
||||
|
||||
bool VisitCXXConstCastExpr(CXXConstCastExpr const * expr);
|
||||
@@ -205,6 +280,48 @@ bool RedundantCast::VisitImplicitCastExpr(const ImplicitCastExpr * expr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RedundantCast::VisitCXXStaticCastExpr(CXXStaticCastExpr const * expr) {
|
||||
if (ignoreLocation(expr)) {
|
||||
return true;
|
||||
}
|
||||
auto t1 = getSubExprAsWritten(expr)->getType();
|
||||
auto t2 = expr->getTypeAsWritten();
|
||||
if (t1.getCanonicalType() != t2.getCanonicalType()
|
||||
|| t1->isArithmeticType())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Don't warn about
|
||||
//
|
||||
// *pResult = static_cast<oslModule>(RTLD_DEFAULT);
|
||||
//
|
||||
// in osl_getModuleHandle (sal/osl/unx/module.cxx) (where oslModule is a
|
||||
// typedef to void *):
|
||||
if (loplugin::TypeCheck(t2).Typedef("oslModule").GlobalNamespace()
|
||||
&& !loplugin::TypeCheck(t1).Typedef())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Dont't warn about
|
||||
//
|
||||
// curl_easy_setopt(static_cast<CURL*>(pData),
|
||||
// CURLOPT_HEADERFUNCTION,
|
||||
// memory_write_dummy);
|
||||
//
|
||||
// in delete_CURL (ucb/source/ucp/ftp/ftploaderthread.cxx) (where CURL is a
|
||||
// typedef to void):
|
||||
if (loplugin::TypeCheck(t2).Pointer().Typedef("CURL").GlobalNamespace()
|
||||
&& !loplugin::TypeCheck(t1).Pointer().Typedef())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
report(
|
||||
DiagnosticsEngine::Warning,
|
||||
"redundant static_cast from %0 to %1", expr->getExprLoc())
|
||||
<< t1 << t2 << expr->getSourceRange();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RedundantCast::VisitCXXReinterpretCastExpr(
|
||||
CXXReinterpretCastExpr const * expr)
|
||||
{
|
||||
|
Reference in New Issue
Block a user