summaryrefslogtreecommitdiff
path: root/compilerplugins
diff options
context:
space:
mode:
authorStephan Bergmann <sbergman@redhat.com>2017-07-03 12:34:38 +0200
committerStephan Bergmann <sbergman@redhat.com>2017-07-03 12:34:38 +0200
commit65d6c642590bd5f51c04228d941608322a85f1ac (patch)
treeb0eec098731abbc9e89ae7b33e52dc3450cffffc /compilerplugins
parente6391dde04044147a1ac13acdccf797f3872fc6b (diff)
loplugin:casttovoid
Change-Id: I427b15b35ef6e7c803cb8a00c961d35175ae8cb2
Diffstat (limited to 'compilerplugins')
-rw-r--r--compilerplugins/clang/casttovoid.cxx494
-rw-r--r--compilerplugins/clang/check.cxx73
-rw-r--r--compilerplugins/clang/check.hxx2
-rw-r--r--compilerplugins/clang/compat.hxx34
-rw-r--r--compilerplugins/clang/test/casttovoid.cxx110
-rw-r--r--compilerplugins/clang/unusedvariablecheck.cxx74
6 files changed, 714 insertions, 73 deletions
diff --git a/compilerplugins/clang/casttovoid.cxx b/compilerplugins/clang/casttovoid.cxx
new file mode 100644
index 000000000000..997f7ed83e7c
--- /dev/null
+++ b/compilerplugins/clang/casttovoid.cxx
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <stack>
+
+#include "clang/AST/Attr.h"
+
+#include "check.hxx"
+#include "compat.hxx"
+#include "plugin.hxx"
+
+namespace {
+
+bool isWarnUnusedType(QualType type) {
+ if (auto const t = type->getAs<TypedefType>()) {
+ if (t->getDecl()->hasAttr<WarnUnusedAttr>()) {
+ return true;
+ }
+ }
+ if (auto const t = type->getAs<RecordType>()) {
+ if (t->getDecl()->hasAttr<WarnUnusedAttr>()) {
+ return true;
+ }
+ }
+ return loplugin::isExtraWarnUnusedType(type);
+}
+
+Expr const * lookThroughInitListExpr(Expr const * expr) {
+ if (auto const ile = dyn_cast<InitListExpr>(expr->IgnoreParenImpCasts())) {
+ if (ile->getNumInits() == 1) {
+ return ile->getInit(0);
+ }
+ }
+ return expr;
+}
+
+class Visitor final:
+ public RecursiveASTVisitor<Visitor>, public loplugin::Plugin
+{
+public:
+ explicit Visitor(InstantiationData const & data): Plugin(data) {}
+
+ bool TraverseCStyleCastExpr(CStyleCastExpr * expr) {
+ auto const dre = checkCast(expr);
+ if (dre != nullptr) {
+ castToVoid_.push({expr, dre});
+ }
+ auto const ret = RecursiveASTVisitor::TraverseCStyleCastExpr(expr);
+ if (dre != nullptr) {
+ assert(!castToVoid_.empty());
+ assert(castToVoid_.top().cast == expr);
+ assert(castToVoid_.top().sub == dre);
+ castToVoid_.pop();
+ }
+ return ret;
+ }
+
+ bool TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr) {
+ auto const dre = checkCast(expr);
+ if (dre != nullptr) {
+ castToVoid_.push({expr, dre});
+ }
+ auto const ret = RecursiveASTVisitor::TraverseCXXStaticCastExpr(expr);
+ if (dre != nullptr) {
+ assert(!castToVoid_.empty());
+ assert(castToVoid_.top().cast == expr);
+ assert(castToVoid_.top().sub == dre);
+ castToVoid_.pop();
+ }
+ return ret;
+ }
+
+ bool TraverseCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr) {
+ auto const dre = checkCast(expr);
+ if (dre != nullptr) {
+ castToVoid_.push({expr, dre});
+ }
+ auto const ret = RecursiveASTVisitor::TraverseCXXFunctionalCastExpr(
+ expr);
+ if (dre != nullptr) {
+ assert(!castToVoid_.empty());
+ assert(castToVoid_.top().cast == expr);
+ assert(castToVoid_.top().sub == dre);
+ castToVoid_.pop();
+ }
+ return ret;
+ }
+
+ bool TraverseFunctionDecl(FunctionDecl * decl) {
+ returnTypes_.push(decl->getReturnType());
+ auto const ret = RecursiveASTVisitor::TraverseFunctionDecl(decl);
+ assert(!returnTypes_.empty());
+ assert(returnTypes_.top() == decl->getReturnType());
+ returnTypes_.pop();
+ return ret;
+ }
+
+#if CLANG_VERSION >= 50000
+ bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl * decl) {
+ returnTypes_.push(decl->getReturnType());
+ auto const ret = RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(
+ decl);
+ assert(!returnTypes_.empty());
+ assert(returnTypes_.top() == decl->getReturnType());
+ returnTypes_.pop();
+ return ret;
+ }
+#endif
+
+ bool TraverseCXXMethodDecl(CXXMethodDecl * decl) {
+ returnTypes_.push(decl->getReturnType());
+ auto const ret = RecursiveASTVisitor::TraverseCXXMethodDecl(decl);
+ assert(!returnTypes_.empty());
+ assert(returnTypes_.top() == decl->getReturnType());
+ returnTypes_.pop();
+ return ret;
+ }
+
+ bool TraverseCXXConstructorDecl(CXXConstructorDecl * decl) {
+ returnTypes_.push(decl->getReturnType());
+ auto const ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(decl);
+ assert(!returnTypes_.empty());
+ assert(returnTypes_.top() == decl->getReturnType());
+ returnTypes_.pop();
+ return ret;
+ }
+
+ bool TraverseCXXDestructorDecl(CXXDestructorDecl * decl) {
+ returnTypes_.push(decl->getReturnType());
+ auto const ret = RecursiveASTVisitor::TraverseCXXDestructorDecl(decl);
+ assert(!returnTypes_.empty());
+ assert(returnTypes_.top() == decl->getReturnType());
+ returnTypes_.pop();
+ return ret;
+ }
+
+ bool TraverseCXXConversionDecl(CXXConversionDecl * decl) {
+ returnTypes_.push(decl->getReturnType());
+ auto const ret = RecursiveASTVisitor::TraverseCXXConversionDecl(decl);
+ assert(!returnTypes_.empty());
+ assert(returnTypes_.top() == decl->getReturnType());
+ returnTypes_.pop();
+ return ret;
+ }
+
+ bool TraverseConstructorInitializer(CXXCtorInitializer * init) {
+ if (auto const field = init->getAnyMember()) {
+ if (loplugin::TypeCheck(field->getType()).LvalueReference()) {
+ recordConsumption(lookThroughInitListExpr(init->getInit()));
+ }
+ }
+ return RecursiveASTVisitor::TraverseConstructorInitializer(init);
+ }
+
+ bool VisitDeclRefExpr(DeclRefExpr const * expr) {
+ if (ignoreLocation(expr)) {
+ return true;
+ }
+ auto const var = dyn_cast<VarDecl>(expr->getDecl());
+ if (var == nullptr) {
+ return true;
+ }
+ auto & usage = vars_[var->getCanonicalDecl()];
+ if (!castToVoid_.empty() && castToVoid_.top().sub == expr) {
+ usage.castToVoid.push_back(castToVoid_.top().cast);
+ } else {
+ usage.mentioned = true;
+ }
+ return true;
+ }
+
+ bool VisitImplicitCastExpr(ImplicitCastExpr const * expr) {
+ if (ignoreLocation(expr)) {
+ return true;
+ }
+ if (expr->getCastKind() != CK_LValueToRValue) {
+ return true;
+ }
+ recordConsumption(expr->getSubExpr());
+ return true;
+ }
+
+ bool VisitCallExpr(CallExpr const * expr) {
+ if (ignoreLocation(expr)) {
+ return true;
+ }
+ unsigned firstArg = 0;
+ if (auto const cmce = dyn_cast<CXXMemberCallExpr>(expr)) {
+ recordConsumption(cmce->getImplicitObjectArgument());
+ } else if (isa<CXXOperatorCallExpr>(expr)) {
+ auto const dc = expr->getDirectCallee();
+ if (dc != nullptr && isa<CXXMethodDecl>(dc)) {
+ assert(expr->getNumArgs() != 0);
+ recordConsumption(expr->getArg(0));
+ firstArg = 1;
+ }
+ }
+ auto fun = expr->getDirectCallee();
+ if (fun == nullptr) {
+ return true;
+ }
+ unsigned const n = std::min(fun->getNumParams(), expr->getNumArgs());
+ for (unsigned i = firstArg; i < n; ++i) {
+ if (!loplugin::TypeCheck(fun->getParamDecl(i)->getType())
+ .LvalueReference().Const())
+ {
+ continue;
+ }
+ recordConsumption(lookThroughInitListExpr(expr->getArg(i)));
+ }
+ return true;
+ }
+
+ bool VisitCXXConstructExpr(CXXConstructExpr const * expr) {
+ if (ignoreLocation(expr)) {
+ return true;
+ }
+ auto const ctor = expr->getConstructor();
+ unsigned const n = std::min(ctor->getNumParams(), expr->getNumArgs());
+ for (unsigned i = 0; i != n; ++i) {
+ if (!loplugin::TypeCheck(ctor->getParamDecl(i)->getType())
+ .LvalueReference().Const())
+ {
+ continue;
+ }
+ recordConsumption(lookThroughInitListExpr(expr->getArg(i)));
+ }
+ return true;
+ }
+
+ bool VisitReturnStmt(ReturnStmt const * stmt) {
+ if (ignoreLocation(stmt)) {
+ return true;
+ }
+ assert(!returnTypes_.empty());
+ if (!loplugin::TypeCheck(returnTypes_.top()).LvalueReference().Const())
+ {
+ return true;
+ }
+ auto const ret = stmt->getRetValue();
+ if (ret == nullptr) {
+ return true;
+ }
+ recordConsumption(lookThroughInitListExpr(ret));
+ return true;
+ }
+
+ bool VisitVarDecl(VarDecl const * decl) {
+ if (ignoreLocation(decl)) {
+ return true;
+ }
+ if (!loplugin::TypeCheck(decl->getType()).LvalueReference()) {
+ return true;
+ }
+ auto const init = decl->getInit();
+ if (init == nullptr) {
+ return true;
+ }
+ recordConsumption(lookThroughInitListExpr(init));
+ return true;
+ }
+
+ bool VisitFieldDecl(FieldDecl const * decl) {
+ if (ignoreLocation(decl)) {
+ return true;
+ }
+ if (!loplugin::TypeCheck(decl->getType()).LvalueReference()) {
+ return true;
+ }
+ auto const init = decl->getInClassInitializer();
+ if (init == nullptr) {
+ return true;
+ }
+ recordConsumption(lookThroughInitListExpr(init));
+ return true;
+ }
+
+private:
+ struct Usage {
+ std::vector<ExplicitCastExpr const *> castToVoid;
+ bool mentioned = false;
+ DeclRefExpr const * firstConsumption = nullptr;
+ };
+
+ struct CastToVoid {
+ ExplicitCastExpr const * cast;
+ DeclRefExpr const * sub;
+ };
+
+ std::map<VarDecl const *, Usage> vars_;
+ std::stack<CastToVoid> castToVoid_;
+ std::stack<QualType> returnTypes_;
+
+ void run() override {
+ if (!TraverseDecl(compiler.getASTContext().getTranslationUnitDecl())) {
+ return;
+ }
+ for (auto const & i: vars_) {
+ if (i.second.firstConsumption == nullptr) {
+ if (i.second.mentioned) {
+ continue;
+ }
+ if (isa<ParmVarDecl>(i.first)) {
+ if (!compiler.getLangOpts().CPlusPlus
+ || isSharedCAndCppCode(i.first))
+ {
+ continue;
+ }
+ auto const fun = dyn_cast_or_null<FunctionDecl>(
+ i.first->getDeclContext());
+ assert(fun != nullptr);
+ if (containsPreprocessingConditionalInclusion(
+ fun->getSourceRange()))
+ {
+ continue;
+ }
+ auto const meth = dyn_cast<CXXMethodDecl>(fun);
+ report(
+ DiagnosticsEngine::Warning,
+ "unused%select{| virtual function}0 parameter name",
+ i.first->getLocation())
+ << (meth != nullptr && meth->isVirtual())
+ << i.first->getSourceRange();
+ for (auto const j: i.second.castToVoid) {
+ report(
+ DiagnosticsEngine::Note, "cast to void here",
+ j->getExprLoc())
+ << j->getSourceRange();
+ }
+ } else if (!i.second.castToVoid.empty()
+ && !isWarnUnusedType(i.first->getType()))
+ {
+ report(
+ DiagnosticsEngine::Warning,
+ "unused variable %select{declaration|name}0",
+ i.first->getLocation())
+ << i.first->isExceptionVariable()
+ << i.first->getSourceRange();
+ for (auto const j: i.second.castToVoid) {
+ report(
+ DiagnosticsEngine::Note, "cast to void here",
+ j->getExprLoc())
+ << j->getSourceRange();
+ }
+ }
+ } else {
+ for (auto const j: i.second.castToVoid) {
+ report(
+ DiagnosticsEngine::Warning, "unnecessary cast to void",
+ j->getExprLoc())
+ << j->getSourceRange();
+ report(
+ DiagnosticsEngine::Note, "first consumption is here",
+ i.second.firstConsumption->getExprLoc())
+ << i.second.firstConsumption->getSourceRange();
+ }
+ }
+ }
+ }
+
+ bool isFromCIncludeFile(SourceLocation spellingLocation) const {
+ return !compiler.getSourceManager().isInMainFile(spellingLocation)
+ && (StringRef(
+ compiler.getSourceManager().getPresumedLoc(spellingLocation)
+ .getFilename())
+ .endswith(".h"));
+ }
+
+ bool isSharedCAndCppCode(VarDecl const * decl) const {
+ auto loc = decl->getLocStart();
+ while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
+ loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
+ }
+ // Assume that code is intended to be shared between C and C++ if it
+ // comes from an include file ending in .h, and is either in an extern
+ // "C" context or the body of a macro definition:
+ return
+ isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(loc))
+ && (decl->isInExternCContext()
+ || compiler.getSourceManager().isMacroBodyExpansion(loc));
+ }
+
+ bool containsPreprocessingConditionalInclusion(SourceRange range) {
+ auto hash = false;
+ for (auto loc = range.getBegin();;) {
+ Token tok;
+ if (Lexer::getRawToken(
+ loc, tok, compiler.getSourceManager(),
+ compiler.getLangOpts(), true))
+ {
+ // Conservatively assume "yes" if lexing fails (e.g., due to
+ // macros):
+ return true;
+ }
+ if (hash && tok.is(tok::raw_identifier)) {
+ auto const id = tok.getRawIdentifier();
+ if (id == "if" || id == "ifdef" || id == "ifndef"
+ || id == "elif" || id == "else" || id == "endif")
+ {
+ return true;
+ }
+ }
+ if (loc == range.getEnd()) {
+ break;
+ }
+ hash = tok.is(tok::hash) && tok.isAtStartOfLine();
+ loc = loc.getLocWithOffset(
+ std::max<unsigned>(
+ Lexer::MeasureTokenLength(
+ loc, compiler.getSourceManager(),
+ compiler.getLangOpts()),
+ 1));
+ }
+ return false;
+ }
+
+ DeclRefExpr const * checkCast(ExplicitCastExpr const * expr) {
+ if (!loplugin::TypeCheck(expr->getTypeAsWritten()).Void()) {
+ return nullptr;
+ }
+ if (compiler.getSourceManager().isMacroBodyExpansion(
+ expr->getLocStart()))
+ {
+ return nullptr;
+ }
+ return dyn_cast<DeclRefExpr>(expr->getSubExpr()->IgnoreParenImpCasts());
+ }
+
+ void recordConsumption(Expr const * expr) {
+ for (;;) {
+ expr = expr->IgnoreParenImpCasts();
+ if (auto const e = dyn_cast<MemberExpr>(expr)) {
+ expr = e->getBase();
+ continue;
+ }
+ if (auto const e = dyn_cast<ArraySubscriptExpr>(expr)) {
+ expr = e->getBase();
+ continue;
+ }
+ if (auto const e = dyn_cast<BinaryOperator>(expr)) {
+ if (e->getOpcode() == BO_PtrMemD) {
+ expr = e->getLHS();
+ continue;
+ }
+ }
+ break;
+ }
+ auto const dre = dyn_cast<DeclRefExpr>(expr);
+ if (dre == nullptr) {
+ return;
+ }
+ // In C (but not in C++)
+ //
+ // (void) x
+ //
+ // contains an implicit lvalue-to-rvalue cast, so VisitImplicitCastExpr
+ // would record that as a consumption if we didn't filter it out here:
+ if (!castToVoid_.empty() && castToVoid_.top().sub == dre) {
+ return;
+ }
+ auto const var = dyn_cast<VarDecl>(dre->getDecl());
+ if (var == nullptr) {
+ return;
+ }
+ auto & usage = vars_[var->getCanonicalDecl()];
+ if (usage.firstConsumption != nullptr) {
+ return;
+ }
+ auto const loc = dre->getLocStart();
+ if (compiler.getSourceManager().isMacroArgExpansion(loc)
+ && (compat::getImmediateMacroNameForDiagnostics(
+ loc, compiler.getSourceManager(), compiler.getLangOpts())
+ == "assert"))
+ {
+ return;
+ }
+ usage.firstConsumption = dre;
+ }
+};
+
+static loplugin::Plugin::Registration<Visitor> reg("casttovoid");
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/compilerplugins/clang/check.cxx b/compilerplugins/clang/check.cxx
index f7647ac8c9c9..1ea251b2b226 100644
--- a/compilerplugins/clang/check.cxx
+++ b/compilerplugins/clang/check.cxx
@@ -161,6 +161,79 @@ ContextCheck ContextCheck::AnonymousNamespace() const {
n != nullptr && n->isAnonymousNamespace() ? n->getParent() : nullptr);
}
+namespace {
+
+bool BaseCheckNotSomethingInterestingSubclass(
+ const clang::CXXRecordDecl *BaseDefinition
+#if CLANG_VERSION < 30800
+ , void *
+#endif
+ )
+{
+ if (BaseDefinition) {
+ auto tc = TypeCheck(BaseDefinition);
+ if (tc.Class("Dialog").GlobalNamespace() || tc.Class("SfxPoolItem").GlobalNamespace()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool isDerivedFromSomethingInteresting(const clang::CXXRecordDecl *decl) {
+ if (!decl)
+ return false;
+ auto tc = TypeCheck(decl);
+ if (tc.Class("Dialog"))
+ return true;
+ if (tc.Class("SfxPoolItem"))
+ return true;
+ if (!decl->hasDefinition()) {
+ return false;
+ }
+ if (// not sure what hasAnyDependentBases() does,
+ // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
+ !decl->hasAnyDependentBases() &&
+ !compat::forallBases(*decl, BaseCheckNotSomethingInterestingSubclass, nullptr, true)) {
+ return true;
+ }
+ return false;
+}
+
+}
+
+bool isExtraWarnUnusedType(clang::QualType type) {
+ auto const rec = type->getAsCXXRecordDecl();
+ if (rec == nullptr) {
+ return false;
+ }
+ if (rec->hasAttrs()) {
+ // Clang currently has no support for custom attributes, but the
+ // annotate attribute comes close, so check for
+ // __attribute__((annotate("lo_warn_unused"))):
+ for (auto i = rec->specific_attr_begin<clang::AnnotateAttr>(),
+ e = rec->specific_attr_end<clang::AnnotateAttr>();
+ i != e; ++i) {
+ if ((*i)->getAnnotation() == "lo_warn_unused") {
+ return true;
+ }
+ }
+ }
+ auto const tc = TypeCheck(rec);
+ // Check some common non-LO types:
+ if (tc.Class("string").Namespace("std").GlobalNamespace()
+ || tc.Class("basic_string").Namespace("std").GlobalNamespace()
+ || tc.Class("list").Namespace("std").GlobalNamespace()
+ || (tc.Class("list").Namespace("__debug").Namespace("std")
+ .GlobalNamespace())
+ || tc.Class("vector").Namespace("std").GlobalNamespace()
+ || (tc.Class("vector" ).Namespace("__debug").Namespace("std")
+ .GlobalNamespace()))
+ {
+ return true;
+ }
+ return isDerivedFromSomethingInteresting(rec);
+}
+
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/compilerplugins/clang/check.hxx b/compilerplugins/clang/check.hxx
index d044db10b5e6..e2867c8512d0 100644
--- a/compilerplugins/clang/check.hxx
+++ b/compilerplugins/clang/check.hxx
@@ -279,6 +279,8 @@ template<std::size_t N> ContextCheck ContextCheck::Struct(char const (& id)[N])
llvm::dyn_cast_or_null<clang::Decl>(context_), clang::TTK_Struct, id);
}
+bool isExtraWarnUnusedType(clang::QualType type);
+
}
#endif
diff --git a/compilerplugins/clang/compat.hxx b/compilerplugins/clang/compat.hxx
index a493cdc7b209..060bf4cf2317 100644
--- a/compilerplugins/clang/compat.hxx
+++ b/compilerplugins/clang/compat.hxx
@@ -214,6 +214,40 @@ inline bool isMacroArgExpansion(
#endif
}
+inline llvm::StringRef getImmediateMacroNameForDiagnostics(
+ clang::SourceLocation Loc, clang::SourceManager const & SM,
+ clang::LangOptions const &LangOpts)
+{
+#if CLANG_VERSION >= 30900
+ return clang::Lexer::getImmediateMacroNameForDiagnostics(Loc, SM, LangOpts);
+#else
+ using namespace clang;
+ // Verbatim copy from Clang's lib/Lex/Lexer.cpp:
+
+ assert(Loc.isMacroID() && "Only reasonble to call this on macros");
+ // Walk past macro argument expanions.
+ while (SM.isMacroArgExpansion(Loc))
+ Loc = SM.getImmediateExpansionRange(Loc).first;
+
+ // If the macro's spelling has no FileID, then it's actually a token paste
+ // or stringization (or similar) and not a macro at all.
+ if (!SM.getFileEntryForID(SM.getFileID(SM.getSpellingLoc(Loc))))
+ return StringRef();
+
+ // Find the spelling location of the start of the non-argument expansion
+ // range. This is where the macro name was spelled in order to begin
+ // expanding this macro.
+ Loc = SM.getSpellingLoc(SM.getImmediateExpansionRange(Loc).first);
+
+ // Dig out the buffer where the macro name was spelled and the extents of
+ // the name so that we can render it into the expansion note.
+ std::pair<FileID, unsigned> ExpansionInfo = SM.getDecomposedLoc(Loc);
+ unsigned MacroTokenLength = Lexer::MeasureTokenLength(Loc, SM, LangOpts);
+ StringRef ExpansionBuffer = SM.getBufferData(ExpansionInfo.first);
+ return ExpansionBuffer.substr(ExpansionInfo.second, MacroTokenLength);
+#endif
+}
+
inline auto getAsTagDecl(clang::Type const& t) -> clang::TagDecl *
{
#if CLANG_VERSION >= 30500
diff --git a/compilerplugins/clang/test/casttovoid.cxx b/compilerplugins/clang/test/casttovoid.cxx
new file mode 100644
index 000000000000..edd538690224
--- /dev/null
+++ b/compilerplugins/clang/test/casttovoid.cxx
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+
+#include <cassert>
+
+#define CAST_N3 (void) n3
+#define ASSERT_N4 assert(n4 == 0)
+#define ASSERT(x) assert(x)
+#define USE(x) x
+
+int f1(int n1, int n2, int n3, int n4, int n5) {
+ (void) n1; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ int const & r1 = n1; // expected-note {{first consumption is here [loplugin:casttovoid]}}
+ (void) n2; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ int const & r2 = {n2}; // expected-note {{first consumption is here [loplugin:casttovoid]}}
+ (void) n3; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ int const & r3{n3}; // expected-note {{first consumption is here [loplugin:casttovoid]}}
+ (void) n4; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ int const & r4(n4); // expected-note {{first consumption is here [loplugin:casttovoid]}}
+ (void) n5; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ int const & r5 = (n5); // expected-note {{first consumption is here [loplugin:casttovoid]}}
+ return r1 + r2 + r3 + r4 + r5;
+}
+
+int const & f2(int const & n) {
+ (void) n; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ return n; // expected-note {{first consumption is here [loplugin:casttovoid]}}
+}
+
+int const & f3(int const & n) {
+ (void) n; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ return (n); // expected-note {{first consumption is here [loplugin:casttovoid]}}
+}
+
+int const & f4(int const & n) {
+ (void) n; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ return {n}; // expected-note {{first consumption is here [loplugin:casttovoid]}}
+}
+
+int const & f5(int const & n) {
+ (void) n; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ return {(n)}; // expected-note {{first consumption is here [loplugin:casttovoid]}}
+}
+
+struct S1 {
+ S1(int n1, int n2):
+ n1_(n1), // expected-note {{first consumption is here [loplugin:casttovoid]}}
+ n2_{n2} // expected-note {{first consumption is here [loplugin:casttovoid]}}
+ {
+ (void) n1; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ (void) n2; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ }
+ int const & n1_;
+ int const & n2_;
+};
+
+struct S2 { int n; };
+
+int fS2_1(S2 s) {
+ (void) s; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ return s.n; // expected-note {{first consumption is here [loplugin:casttovoid]}}
+}
+
+int const & fS2_2(S2 const & s) {
+ (void) s; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ return s.n; // expected-note {{first consumption is here [loplugin:casttovoid]}}
+}
+
+int main() {
+ int n1 = 0;
+ (void) n1; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ (void const) n1; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ (void volatile) n1; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ (void const volatile) n1; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ (void) (n1); // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ (void) ((n1)); // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ (void(n1)); // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ static_cast<void>(n1); // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ int n2 = 0;
+ assert(n2 == 0);
+ (void) n2; // expected-error {{unnecessary cast to void [loplugin:casttovoid]}}
+ int n3 = 0;
+ CAST_N3;
+ int n4 = 0;
+ ASSERT_N4;
+ (void) n4;
+ int n5 = 0;
+ assert(n5 == 0);
+ (void) n5;
+ int n6 = 0;
+ ASSERT(n6 == 0);
+ (void) n6;
+ int n7 = 0;
+ assert(USE(n7) == 0);
+ (void) n7;
+ int n8 = 0;
+ ASSERT(USE(USE(n8 == 0)));
+ (void) n8;
+ return n1 // expected-note 8 {{first consumption is here [loplugin:casttovoid]}}
+ + n2 // expected-note {{first consumption is here [loplugin:casttovoid]}}
+ + n3;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/compilerplugins/clang/unusedvariablecheck.cxx b/compilerplugins/clang/unusedvariablecheck.cxx
index 50b41d5664f8..57b78cca5a69 100644
--- a/compilerplugins/clang/unusedvariablecheck.cxx
+++ b/compilerplugins/clang/unusedvariablecheck.cxx
@@ -20,8 +20,6 @@
#include "check.hxx"
#include "unusedvariablecheck.hxx"
-#include <clang/AST/Attr.h>
-
namespace loplugin
{
@@ -50,42 +48,6 @@ void UnusedVariableCheck::run()
TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
}
-bool BaseCheckNotSomethingInterestingSubclass(
- const CXXRecordDecl *BaseDefinition
-#if CLANG_VERSION < 30800
- , void *
-#endif
- )
-{
- if (BaseDefinition) {
- auto tc = loplugin::TypeCheck(BaseDefinition);
- if (tc.Class("Dialog").GlobalNamespace() || tc.Class("SfxPoolItem").GlobalNamespace()) {
- return false;
- }
- }
- return true;
-}
-
-bool isDerivedFromSomethingInteresting(const CXXRecordDecl *decl) {
- if (!decl)
- return false;
- auto tc = loplugin::TypeCheck(decl);
- if (tc.Class("Dialog"))
- return true;
- if (tc.Class("SfxPoolItem"))
- return true;
- if (!decl->hasDefinition()) {
- return false;
- }
- if (// not sure what hasAnyDependentBases() does,
- // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
- !decl->hasAnyDependentBases() &&
- !compat::forallBases(*decl, BaseCheckNotSomethingInterestingSubclass, nullptr, true)) {
- return true;
- }
- return false;
-}
-
bool UnusedVariableCheck::VisitVarDecl( const VarDecl* var )
{
if( ignoreLocation( var ))
@@ -94,41 +56,8 @@ bool UnusedVariableCheck::VisitVarDecl( const VarDecl* var )
return true;
if( var->isDefinedOutsideFunctionOrMethod())
return true;
- if( CXXRecordDecl* type = var->getType()->getAsCXXRecordDecl())
+ if( loplugin::isExtraWarnUnusedType(var->getType()))
{
- bool warn_unused = false;
- if( type->hasAttrs())
- {
- // Clang currently has no support for custom attributes, but
- // the annotate attribute comes close, so check for __attribute__((annotate("lo_warn_unused")))
- for( specific_attr_iterator<AnnotateAttr> i = type->specific_attr_begin<AnnotateAttr>(),
- e = type->specific_attr_end<AnnotateAttr>();
- i != e;
- ++i )
- {
- if( (*i)->getAnnotation() == "lo_warn_unused" )
- {
- warn_unused = true;
- break;
- }
- }
- }
- if( !warn_unused )
- {
- auto tc = loplugin::TypeCheck(type);
- // Check some common non-LO types.
- if( tc.Class("string").Namespace("std").GlobalNamespace()
- || tc.Class("basic_string").Namespace("std").GlobalNamespace()
- || tc.Class("list").Namespace("std").GlobalNamespace()
- || tc.Class("list").Namespace("__debug").Namespace("std").GlobalNamespace()
- || tc.Class("vector").Namespace("std").GlobalNamespace()
- || tc.Class("vector" ).Namespace("__debug").Namespace("std").GlobalNamespace())
- warn_unused = true;
- if (!warn_unused && isDerivedFromSomethingInteresting(type))
- warn_unused = true;
- }
- if( warn_unused )
- {
if( const ParmVarDecl* param = dyn_cast< ParmVarDecl >( var ))
{
if( !param->getDeclName())
@@ -144,7 +73,6 @@ bool UnusedVariableCheck::VisitVarDecl( const VarDecl* var )
else
report( DiagnosticsEngine::Warning, "unused variable %0",
var->getLocation()) << var->getDeclName();
- }
}
return true;
}