summaryrefslogtreecommitdiff
path: root/compilerplugins
diff options
context:
space:
mode:
authorStephan Bergmann <sbergman@redhat.com>2014-04-15 16:53:51 +0200
committerStephan Bergmann <sbergman@redhat.com>2014-04-15 16:57:23 +0200
commite5199d3d78721c962f53a8675d5245e4b839bdc3 (patch)
tree4fb9d013954199c26ca343dd4874b1f46fdcb071 /compilerplugins
parentc5265586e635d294708f6af10697134a95a26e08 (diff)
Flag unreferrenced functions only declared in the main file, not an include
...which appears to be a good heuristic to identify functions that are either unused or should better be declared just once in an include file. (It also filters out SAL_DLLPUBLIC extern "C" function definitions, which are most likely meant to be referenced dynamically via dlsym.) Change-Id: I7fb78cb836b971791704851535dcfbda2b2f5bc0
Diffstat (limited to 'compilerplugins')
-rw-r--r--compilerplugins/clang/compat.hxx24
-rw-r--r--compilerplugins/clang/unreffun.cxx148
2 files changed, 172 insertions, 0 deletions
diff --git a/compilerplugins/clang/compat.hxx b/compilerplugins/clang/compat.hxx
index 3d265722c044..7389f636d0bf 100644
--- a/compilerplugins/clang/compat.hxx
+++ b/compilerplugins/clang/compat.hxx
@@ -20,7 +20,9 @@
#include "clang/AST/Type.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/Linkage.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Visibility.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_ostream.h"
@@ -52,6 +54,28 @@ inline bool isExternCContext(clang::DeclContext const & ctxt) {
#endif
}
+#if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
+typedef clang::LinkageInfo LinkageInfo;
+#else
+typedef clang::NamedDecl::LinkageInfo LinkageInfo;
+#endif
+
+inline clang::Linkage getLinkage(LinkageInfo const & info) {
+#if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
+ return info.getLinkage();
+#else
+ return info.linkage();
+#endif
+}
+
+inline clang::Visibility getVisibility(LinkageInfo const & info) {
+#if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
+ return info.getVisibility();
+#else
+ return info.visibility();
+#endif
+}
+
inline bool isFirstDecl(clang::FunctionDecl const & decl) {
#if (__clang_major__ == 3 && __clang_minor__ >= 4) || __clang_major__ > 3
return decl.isFirstDecl();
diff --git a/compilerplugins/clang/unreffun.cxx b/compilerplugins/clang/unreffun.cxx
new file mode 100644
index 000000000000..d49ad2917582
--- /dev/null
+++ b/compilerplugins/clang/unreffun.cxx
@@ -0,0 +1,148 @@
+/* -*- 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/.
+ */
+
+#include <cassert>
+#include <string>
+
+#include "clang/AST/Attr.h"
+#include "clang/Sema/SemaInternal.h" // warn_unused_function
+
+#include "compat.hxx"
+#include "plugin.hxx"
+
+namespace {
+
+// It appears that, given a function declaration, there is no way to determine
+// the language linkage of the function's type, only of the function's name
+// (via FunctionDecl::isExternC); however, in a case like
+//
+// extern "C" { static void f(); }
+//
+// the function's name does not have C language linkage while the function's
+// type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
+// 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
+// "Language linkage of function type":
+bool hasCLanguageLinkageType(FunctionDecl const * decl) {
+ assert(decl != nullptr);
+ if (decl->isExternC()) {
+ return true;
+ }
+#if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
+ if (decl->isInExternCContext()) {
+ return true;
+ }
+#else
+ if (decl->getCanonicalDecl()->getDeclContext()->isExternCContext()) {
+ return true;
+ }
+#endif
+ return false;
+}
+
+class UnrefFun: public RecursiveASTVisitor<UnrefFun>, public loplugin::Plugin {
+public:
+ explicit UnrefFun(InstantiationData const & data): Plugin(data) {}
+
+ void run() override
+ { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
+
+ bool VisitFunctionDecl(FunctionDecl const * decl);
+
+private:
+ bool isInUnoIncludeFile(SourceLocation spellingLocation) const;
+};
+
+bool UnrefFun::VisitFunctionDecl(FunctionDecl const * decl) {
+ if (ignoreLocation(decl)) {
+ return true;
+ }
+
+ //TODO, filtering out anything template for now:
+ if (decl->isDependentContext()) {
+ return true;
+ }
+ CXXRecordDecl const * r = dyn_cast<CXXRecordDecl>(decl->getDeclContext());;
+ if (r != nullptr && r->getTemplateSpecializationKind() != TSK_Undeclared) {
+ return true;
+ }
+
+ FunctionDecl const * canon = decl->getCanonicalDecl();
+ //TODO: is that the first?
+ if (canon->isDeleted() || canon->isReferenced()
+ || !(canon->isDefined()
+ ? decl->isThisDeclarationADefinition()
+ : compat::isFirstDecl(*decl))
+ || !compat::isInMainFile(
+ compiler.getSourceManager(), canon->getLocation())
+ || isInUnoIncludeFile(
+ compiler.getSourceManager().getSpellingLoc(
+ canon->getNameInfo().getLoc()))
+ || canon->isMain()
+ || (compiler.getDiagnostics().getDiagnosticLevel(
+ diag::warn_unused_function, decl->getLocation())
+ < DiagnosticsEngine::Warning))
+ {
+ return true;
+ }
+ compat::LinkageInfo info(canon->getLinkageAndVisibility());
+ if (compat::getLinkage(info) == ExternalLinkage
+ && hasCLanguageLinkageType(canon) && canon->isDefined()
+ && ((decl == canon && compat::getVisibility(info) == DefaultVisibility)
+ || ((canon->hasAttr<ConstructorAttr>()
+ || canon->hasAttr<DestructorAttr>())
+ && compat::getVisibility(info) == HiddenVisibility)))
+ {
+ return true;
+ }
+ report(
+ DiagnosticsEngine::Warning,
+ (canon->isDefined()
+#if (__clang_major__ == 3 && __clang_minor__ >= 4) || __clang_major__ > 3
+ ? (canon->isExternallyVisible()
+ ? "Unreferenced externally visible function definition"
+ : "Unreferenced externally invisible function definition")
+#else
+ ? "Unreferenced function definition"
+#endif
+ : "Unreferenced function declaration"),
+ decl->getLocation())
+ << decl->getSourceRange();
+ if (canon->isDefined() && !compat::isFirstDecl(*decl)) {
+ report(
+ DiagnosticsEngine::Note, "first declaration is here",
+ canon->getLocation())
+ << canon->getSourceRange();
+ }
+ return true;
+}
+
+bool UnrefFun::isInUnoIncludeFile(SourceLocation spellingLocation) const {
+ StringRef name {
+ compiler.getSourceManager().getFilename(spellingLocation) };
+ return compat::isInMainFile(compiler.getSourceManager(), spellingLocation)
+ ? (name == SRCDIR "/cppu/source/cppu/compat.cxx"
+ || name == SRCDIR "/cppuhelper/source/compat.cxx"
+ || name == SRCDIR "/sal/osl/all/compat.cxx")
+ : (name.startswith(SRCDIR "/include/com/")
+ || name.startswith(SRCDIR "/include/cppu/")
+ || name.startswith(SRCDIR "/include/cppuhelper/")
+ || name.startswith(SRCDIR "/include/osl/")
+ || name.startswith(SRCDIR "/include/rtl/")
+ || name.startswith(SRCDIR "/include/sal/")
+ || name.startswith(SRCDIR "/include/salhelper/")
+ || name.startswith(SRCDIR "/include/systools/")
+ || name.startswith(SRCDIR "/include/typelib/")
+ || name.startswith(SRCDIR "/include/uno/"));
+}
+
+loplugin::Plugin::Registration<UnrefFun> X("unreffun");
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */