diff options
author | Stephan Bergmann <sbergman@redhat.com> | 2017-07-03 12:34:38 +0200 |
---|---|---|
committer | Stephan Bergmann <sbergman@redhat.com> | 2017-07-03 12:34:38 +0200 |
commit | 65d6c642590bd5f51c04228d941608322a85f1ac (patch) | |
tree | b0eec098731abbc9e89ae7b33e52dc3450cffffc /compilerplugins | |
parent | e6391dde04044147a1ac13acdccf797f3872fc6b (diff) |
loplugin:casttovoid
Change-Id: I427b15b35ef6e7c803cb8a00c961d35175ae8cb2
Diffstat (limited to 'compilerplugins')
-rw-r--r-- | compilerplugins/clang/casttovoid.cxx | 494 | ||||
-rw-r--r-- | compilerplugins/clang/check.cxx | 73 | ||||
-rw-r--r-- | compilerplugins/clang/check.hxx | 2 | ||||
-rw-r--r-- | compilerplugins/clang/compat.hxx | 34 | ||||
-rw-r--r-- | compilerplugins/clang/test/casttovoid.cxx | 110 | ||||
-rw-r--r-- | compilerplugins/clang/unusedvariablecheck.cxx | 74 |
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; } |