summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/unreffun.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/unreffun.cxx')
-rw-r--r--compilerplugins/clang/unreffun.cxx199
1 files changed, 199 insertions, 0 deletions
diff --git a/compilerplugins/clang/unreffun.cxx b/compilerplugins/clang/unreffun.cxx
new file mode 100644
index 0000000000..fcb6f04016
--- /dev/null
+++ b/compilerplugins/clang/unreffun.cxx
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ */
+
+#ifndef LO_CLANG_SHARED_PLUGINS
+
+#include <cassert>
+#include <stack>
+#include <string>
+
+#include "clang/AST/Attr.h"
+#include "clang/Sema/SemaInternal.h" // warn_unused_function
+
+#include "compat.hxx"
+#include "plugin.hxx"
+
+namespace {
+
+bool isFriendDecl(Decl const * decl) {
+ return decl->getFriendObjectKind() != Decl::FOK_None;
+}
+
+Decl const * getPreviousNonFriendDecl(Decl const * decl) {
+ for (;;) {
+ decl = decl->getPreviousDecl();
+ if (decl == nullptr || !isFriendDecl(decl)) {
+ return decl;
+ }
+ }
+}
+
+bool isSpecialMemberFunction(FunctionDecl const * decl) {
+ if (auto const ctor = dyn_cast<CXXConstructorDecl>(decl)) {
+ return ctor->isDefaultConstructor() || ctor->isCopyOrMoveConstructor();
+ }
+ if (isa<CXXDestructorDecl>(decl)) {
+ return true;
+ }
+ if (auto const meth = dyn_cast<CXXMethodDecl>(decl)) {
+ return meth->isCopyAssignmentOperator() || meth->isMoveAssignmentOperator();
+ }
+ return false;
+}
+
+class UnrefFun: public loplugin::FilteringPlugin<UnrefFun> {
+public:
+ explicit UnrefFun(loplugin::InstantiationData const & data): FilteringPlugin(data) {}
+
+ void run() override
+ { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
+
+ bool PreTraverseFriendDecl(FriendDecl * decl) {
+ friendFunction.push( dyn_cast_or_null<FunctionDecl>(decl->getFriendDecl()));
+ return true;
+ }
+ bool PostTraverseFriendDecl(FriendDecl *, bool ) {
+ friendFunction.pop();
+ return true;
+ }
+ bool TraverseFriendDecl(FriendDecl * decl) {
+ PreTraverseFriendDecl(decl);
+ auto const ret = RecursiveASTVisitor::TraverseFriendDecl(decl);
+ PostTraverseFriendDecl(decl, ret);
+ return ret;
+ }
+
+ bool VisitFunctionDecl(FunctionDecl const * decl);
+
+private:
+ std::stack<FunctionDecl const *> friendFunction;
+};
+
+bool UnrefFun::VisitFunctionDecl(FunctionDecl const * decl) {
+ if (ignoreLocation(decl)) {
+ return true;
+ }
+
+ //TODO, filtering out any functions relating to (class) templates for now:
+ CXXRecordDecl const * r = dyn_cast<CXXRecordDecl>(decl->getDeclContext());
+ if (r != nullptr
+ && (r->getTemplateSpecializationKind() != TSK_Undeclared
+ || r->isDependentContext()))
+ {
+ return true;
+ }
+ if (!friendFunction.empty() && decl == friendFunction.top()) {
+ if (auto const lex = dyn_cast<CXXRecordDecl>(decl->getLexicalDeclContext())) {
+ if (lex->isDependentContext()) {
+ return true;
+ }
+ }
+ }
+
+ if (!(decl->isThisDeclarationADefinition() || isFriendDecl(decl)
+ || decl->isFunctionTemplateSpecialization()))
+ {
+ Decl const * prev = getPreviousNonFriendDecl(decl);
+ if (prev != nullptr/* && prev != decl->getPrimaryTemplate()*/) {
+ // Workaround for redeclarations that introduce visibility attributes
+ // (as is done with
+ //
+ // SAL_DLLPUBLIC_EXPORT GType lok_doc_view_get_type();
+ //
+ // in libreofficekit/source/gtk/lokdocview.cxx):
+ if (decl->getAttr<VisibilityAttr>() != nullptr
+ && prev->getAttr<VisibilityAttr>() == nullptr)
+ {
+ return true;
+ }
+ report(
+ DiagnosticsEngine::Warning,
+ "redundant function%0 redeclaration", decl->getLocation())
+ << ((decl->getTemplatedKind()
+ == FunctionDecl::TK_FunctionTemplate)
+ ? " template" : "")
+ << decl->getSourceRange();
+ report(
+ DiagnosticsEngine::Note, "previous declaration is here",
+ prev->getLocation())
+ << prev->getSourceRange();
+ return true;
+ }
+ }
+
+ FunctionDecl const * canon = decl->getCanonicalDecl();
+ //TODO: is that the first?
+ if (canon->isDeleted() || canon->isReferenced()
+ || !(canon->isDefined()
+ ? decl->isThisDeclarationADefinition() : decl->isFirstDecl())
+ || !compiler.getSourceManager().isInMainFile(canon->getLocation())
+ || isInUnoIncludeFile(canon)
+ || canon->isMain() || canon->isMSVCRTEntryPoint()
+ || (decl->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate
+ && (decl->getDescribedFunctionTemplate()->spec_begin()
+ != decl->getDescribedFunctionTemplate()->spec_end()))
+ || (compiler.getDiagnostics().getDiagnosticLevel(
+ diag::warn_unused_function, decl->getLocation())
+ < DiagnosticsEngine::Warning))
+ {
+ return true;
+ }
+ if (canon->isExplicitlyDefaulted() && isSpecialMemberFunction(canon)) {
+ // If a special member function is explicitly defaulted on the first declaration, assume
+ // that its presence is always due to some interface design consideration, not to explicitly
+ // request a definition that might be worth to flag as unused (and C++20 may extend
+ // defaultability beyond special member functions to comparison operators, therefore
+ // explicitly check here for special member functions only):
+ return true;
+ }
+ LinkageInfo info(canon->getLinkageAndVisibility());
+ if (info.getLinkage() == compat::Linkage::External
+ && loplugin::hasCLanguageLinkageType(canon) && canon->isDefined()
+ && ((decl == canon && info.getVisibility() == DefaultVisibility)
+ || ((canon->hasAttr<ConstructorAttr>()
+ || canon->hasAttr<DestructorAttr>())
+ && info.getVisibility() == HiddenVisibility)))
+ {
+ return true;
+ }
+ auto loc = decl->getLocation();
+ if (compiler.getSourceManager().isMacroBodyExpansion(loc)
+ && (Lexer::getImmediateMacroName(
+ loc, compiler.getSourceManager(), compiler.getLangOpts())
+ == "MDDS_MTV_DEFINE_ELEMENT_CALLBACKS"))
+ {
+ return true;
+ }
+ report(
+ DiagnosticsEngine::Warning,
+ (canon->isDefined()
+ ? (canon->isExternallyVisible()
+ ? "Unreferenced externally visible function%0 definition"
+ : "Unreferenced externally invisible function%0 definition")
+ : "Unreferenced function%0 declaration"),
+ decl->getLocation())
+ << (decl->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate
+ ? " template" : "")
+ << decl->getSourceRange();
+ if (canon->isDefined() && !decl->isFirstDecl()) {
+ report(
+ DiagnosticsEngine::Note, "first declaration is here",
+ canon->getLocation())
+ << canon->getSourceRange();
+ }
+ return true;
+}
+
+loplugin::Plugin::Registration<UnrefFun> unreffun("unreffun");
+
+}
+
+#endif // LO_CLANG_SHARED_PLUGINS
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */