summaryrefslogtreecommitdiff
path: root/compilerplugins
diff options
context:
space:
mode:
authorStephan Bergmann <sbergman@redhat.com>2016-07-08 16:46:57 +0200
committerStephan Bergmann <sbergman@redhat.com>2016-07-08 16:47:31 +0200
commit9d80c032febaaf72b0991ea7add30af506af820a (patch)
treeeedc2fd2ea68b85f96d2400312f43af57a28a922 /compilerplugins
parent1257344e89e80715c35e621b7693f401f147bdf1 (diff)
loplugin:redundantcast: redundant static_casts
Change-Id: I4d50b77745d68a23136221ef06f327137e89fa7e
Diffstat (limited to 'compilerplugins')
-rw-r--r--compilerplugins/clang/check.cxx9
-rw-r--r--compilerplugins/clang/check.hxx21
-rw-r--r--compilerplugins/clang/redundantcast.cxx117
3 files changed, 147 insertions, 0 deletions
diff --git a/compilerplugins/clang/check.cxx b/compilerplugins/clang/check.cxx
index af9532620920..33be502c3e1c 100644
--- a/compilerplugins/clang/check.cxx
+++ b/compilerplugins/clang/check.cxx
@@ -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()
diff --git a/compilerplugins/clang/check.hxx b/compilerplugins/clang/check.hxx
index d69acc264243..0c2c1bb3031f 100644
--- a/compilerplugins/clang/check.hxx
+++ b/compilerplugins/clang/check.hxx
@@ -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
{
diff --git a/compilerplugins/clang/redundantcast.cxx b/compilerplugins/clang/redundantcast.cxx
index 53d4b5b1d9a5..5215b39b18e5 100644
--- a/compilerplugins/clang/redundantcast.cxx
+++ b/compilerplugins/clang/redundantcast.cxx
@@ -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)
{