[Libreoffice-commits] core.git: compilerplugins/clang solenv/CompilerTest_compilerplugins_clang.mk

Stephan Bergmann sbergman at redhat.com
Mon Jul 3 10:35:07 UTC 2017


 compilerplugins/clang/casttovoid.cxx          |  494 ++++++++++++++++++++++++++
 compilerplugins/clang/check.cxx               |   73 +++
 compilerplugins/clang/check.hxx               |    2 
 compilerplugins/clang/compat.hxx              |   34 +
 compilerplugins/clang/test/casttovoid.cxx     |  110 +++++
 compilerplugins/clang/unusedvariablecheck.cxx |   74 ---
 solenv/CompilerTest_compilerplugins_clang.mk  |    1 
 7 files changed, 715 insertions(+), 73 deletions(-)

New commits:
commit 65d6c642590bd5f51c04228d941608322a85f1ac
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Mon Jul 3 12:34:38 2017 +0200

    loplugin:casttovoid
    
    Change-Id: I427b15b35ef6e7c803cb8a00c961d35175ae8cb2

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;
     }
diff --git a/solenv/CompilerTest_compilerplugins_clang.mk b/solenv/CompilerTest_compilerplugins_clang.mk
index 4ae981eb5895..fbddd2580eda 100644
--- a/solenv/CompilerTest_compilerplugins_clang.mk
+++ b/solenv/CompilerTest_compilerplugins_clang.mk
@@ -10,6 +10,7 @@
 $(eval $(call gb_CompilerTest_CompilerTest,compilerplugins_clang))
 
 $(eval $(call gb_CompilerTest_add_exception_objects,compilerplugins_clang, \
+    compilerplugins/clang/test/casttovoid \
     compilerplugins/clang/test/cppunitassertequals \
     compilerplugins/clang/test/datamembershadow \
     compilerplugins/clang/test/externvar \


More information about the Libreoffice-commits mailing list