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

Stephan Bergmann sbergman at redhat.com
Tue Apr 15 07:57:52 PDT 2014


 compilerplugins/clang/compat.hxx   |   24 ++++++
 compilerplugins/clang/unreffun.cxx |  148 +++++++++++++++++++++++++++++++++++++
 2 files changed, 172 insertions(+)

New commits:
commit e5199d3d78721c962f53a8675d5245e4b839bdc3
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Tue Apr 15 16:53:51 2014 +0200

    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

diff --git a/compilerplugins/clang/compat.hxx b/compilerplugins/clang/compat.hxx
index 3d26572..7389f63 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 0000000..d49ad29
--- /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: */


More information about the Libreoffice-commits mailing list