[Libreoffice-commits] core.git: basic/source compilerplugins/clang connectivity/source include/basic include/connectivity include/vcl sw/inc sw/source vcl/source

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Fri Sep 21 16:44:57 UTC 2018


 basic/source/sbx/sbxarray.cxx              |   50 ---
 basic/source/sbx/sbxcoll.cxx               |   13 
 basic/source/sbx/sbxobj.cxx                |   32 --
 compilerplugins/clang/methodcycles.cxx     |  382 +++++++++++++++++++++++++++++
 compilerplugins/clang/methodcycles.py      |  242 ++++++++++++++++++
 compilerplugins/clang/methodcycles.results |   65 ++++
 compilerplugins/clang/unusedmethods.cxx    |   13 
 connectivity/source/parse/sqliterator.cxx  |   67 -----
 include/basic/sbx.hxx                      |    2 
 include/basic/sbxobj.hxx                   |    1 
 include/connectivity/sqliterator.hxx       |    5 
 include/vcl/outdev.hxx                     |    2 
 sw/inc/dbgoutsw.hxx                        |    1 
 sw/source/core/doc/dbgoutsw.cxx            |   71 -----
 vcl/source/outdev/pixel.cxx                |   67 -----
 15 files changed, 698 insertions(+), 315 deletions(-)

New commits:
commit 4c945b22fc42eb7a52864018cbca88358e71fd4d
Author:     Noel Grandin <noel.grandin at collabora.co.uk>
AuthorDate: Fri Sep 21 14:26:56 2018 +0200
Commit:     Noel Grandin <noel.grandin at collabora.co.uk>
CommitDate: Fri Sep 21 18:44:29 2018 +0200

    new loplugin:methodcycles
    
    look for closed cycles of methods i.e. unused code that would not
    otherwise be found by the unusedmethods loplugin
    
    Change-Id: I3fb052132d97aabca573bb8e9fc424201b1e2042
    Reviewed-on: https://gerrit.libreoffice.org/60875
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.grandin at collabora.co.uk>

diff --git a/basic/source/sbx/sbxarray.cxx b/basic/source/sbx/sbxarray.cxx
index 668b4df03bc5..628c7dc918af 100644
--- a/basic/source/sbx/sbxarray.cxx
+++ b/basic/source/sbx/sbxarray.cxx
@@ -341,56 +341,6 @@ void SbxArray::Merge( SbxArray* p )
     }
 }
 
-// Search of an element via the user data. If the element is
-// object, it will also be scanned.
-
-SbxVariable* SbxArray::FindUserData( sal_uInt32 nData )
-{
-    SbxVariable* p = nullptr;
-    for (auto& rEntry : mVarEntries)
-    {
-        if (!rEntry.mpVar.is())
-            continue;
-
-        if (rEntry.mpVar->IsVisible() && rEntry.mpVar->GetUserData() == nData)
-        {
-            p = rEntry.mpVar.get();
-            p->ResetFlag( SbxFlagBits::ExtFound );
-            break;  // JSM 1995-10-06
-        }
-
-        // Did we have an array/object with extended search?
-        if (rEntry.mpVar->IsSet(SbxFlagBits::ExtSearch))
-        {
-            switch (rEntry.mpVar->GetClass())
-            {
-                case SbxClassType::Object:
-                {
-                    // Objects are not allowed to scan their parent.
-                    SbxFlagBits nOld = rEntry.mpVar->GetFlags();
-                    rEntry.mpVar->ResetFlag(SbxFlagBits::GlobalSearch);
-                    p = static_cast<SbxObject&>(*rEntry.mpVar).FindUserData(nData);
-                    rEntry.mpVar->SetFlags(nOld);
-                }
-                break;
-                case SbxClassType::Array:
-                    // Casting SbxVariable to SbxArray?  Really?
-                    p = reinterpret_cast<SbxArray&>(*rEntry.mpVar).FindUserData(nData);
-                break;
-                default:
-                    ;
-            }
-
-            if (p)
-            {
-                p->SetFlag(SbxFlagBits::ExtFound);
-                break;
-            }
-        }
-    }
-    return p;
-}
-
 // Search of an element by his name and type. If an element is an object,
 // it will also be scanned..
 
diff --git a/basic/source/sbx/sbxcoll.cxx b/basic/source/sbx/sbxcoll.cxx
index a4175c310318..ab69d3e032a6 100644
--- a/basic/source/sbx/sbxcoll.cxx
+++ b/basic/source/sbx/sbxcoll.cxx
@@ -87,19 +87,6 @@ void SbxCollection::Initialize()
     p->SetFlag( SbxFlagBits::DontStore );
 }
 
-SbxVariable* SbxCollection::FindUserData( sal_uInt32 nData )
-{
-    if( GetParameters() )
-    {
-        SbxObject* pObj = static_cast<SbxObject*>(GetObject());
-        return pObj ? pObj->FindUserData( nData ) : nullptr;
-    }
-    else
-    {
-        return SbxObject::FindUserData( nData );
-    }
-}
-
 SbxVariable* SbxCollection::Find( const OUString& rName, SbxClassType t )
 {
     if( GetParameters() )
diff --git a/basic/source/sbx/sbxobj.cxx b/basic/source/sbx/sbxobj.cxx
index a9bee2a803ed..d35b2aa0dc16 100644
--- a/basic/source/sbx/sbxobj.cxx
+++ b/basic/source/sbx/sbxobj.cxx
@@ -175,38 +175,6 @@ bool SbxObject::IsClass( const OUString& rName ) const
     return aClassName.equalsIgnoreAsciiCase( rName );
 }
 
-SbxVariable* SbxObject::FindUserData( sal_uInt32 nData )
-{
-    SbxVariable* pRes = pMethods->FindUserData( nData );
-    if( !pRes )
-    {
-        pRes = pProps->FindUserData( nData );
-    }
-    if( !pRes )
-    {
-        pRes = pObjs->FindUserData( nData );
-    }
-    // Search in the parents?
-    if( !pRes && IsSet( SbxFlagBits::GlobalSearch ) )
-    {
-        SbxObject* pCur = this;
-        while( !pRes && pCur->pParent )
-        {
-            // I myself was already searched!
-            SbxFlagBits nOwn = pCur->GetFlags();
-            pCur->ResetFlag( SbxFlagBits::ExtSearch );
-            // I search already global!
-            SbxFlagBits nPar = pCur->pParent->GetFlags();
-            pCur->pParent->ResetFlag( SbxFlagBits::GlobalSearch );
-            pRes = pCur->pParent->FindUserData( nData );
-            pCur->SetFlags( nOwn );
-            pCur->pParent->SetFlags( nPar );
-            pCur = pCur->pParent;
-        }
-    }
-    return pRes;
-}
-
 SbxVariable* SbxObject::Find( const OUString& rName, SbxClassType t )
 {
 #ifdef DBG_UTIL
diff --git a/compilerplugins/clang/methodcycles.cxx b/compilerplugins/clang/methodcycles.cxx
new file mode 100644
index 000000000000..20a31171c001
--- /dev/null
+++ b/compilerplugins/clang/methodcycles.cxx
@@ -0,0 +1,382 @@
+/* -*- 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 <iostream>
+#include <fstream>
+#include <set>
+#include <unordered_set>
+#include <map>
+
+#include "clang/AST/Attr.h"
+
+#include "plugin.hxx"
+#include "compat.hxx"
+
+/**
+What we are looking for here are methods that are not reachable from any of the program
+entry points.
+"Entry points" includes main, and various binary API
+
+Mostly that means we end up finding cycles of methods i.e. methods that refer to each
+other, but are not reachable.
+
+It does so, by dumping various call/definition info to a log file.
+Be warned that it produces around 20G of log file.
+
+Then we will post-process the log file with a python script, which takes about
+15min to run on a fast machine.
+
+The process goes something like this:
+  $ make check
+  $ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='methodcycles' check
+  $ ./compilerplugins/clang/methodcycles.py
+
+Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around
+to get it to work :-)
+
+*/
+
+namespace
+{
+struct MyFuncInfo
+{
+    std::string returnType;
+    std::string nameAndParams;
+    std::string sourceLocation;
+};
+bool operator<(const MyFuncInfo& lhs, const MyFuncInfo& rhs)
+{
+    return std::tie(lhs.returnType, lhs.nameAndParams)
+           < std::tie(rhs.returnType, rhs.nameAndParams);
+}
+
+// try to limit the voluminous output a little
+static std::multimap<const FunctionDecl*, const FunctionDecl*> callMap;
+static std::set<MyFuncInfo> definitionSet;
+
+class MethodCycles : public RecursiveASTVisitor<MethodCycles>, public loplugin::Plugin
+{
+public:
+    explicit MethodCycles(loplugin::InstantiationData const& data)
+        : Plugin(data)
+    {
+    }
+
+    virtual void run() override
+    {
+        TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
+
+        // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
+        // writing to the same logfile
+
+        std::string output;
+        for (const MyFuncInfo& s : definitionSet)
+            output += "definition:\t" + s.returnType + "\t" + s.nameAndParams + "\t"
+                      + s.sourceLocation + "\n";
+        for (const std::pair<const FunctionDecl*, const FunctionDecl*>& pair : callMap)
+        {
+            if (!isLocationMine(pair.first->getLocation())
+                || !isLocationMine(pair.second->getLocation()))
+                continue;
+            auto niceNameFrom = niceName(pair.first);
+            auto niceNameTo = niceName(pair.second);
+            output += "call:\t" + niceNameFrom.returnType + "\t" + niceNameFrom.nameAndParams + "\t"
+                      + niceNameTo.returnType + "\t" + niceNameTo.nameAndParams + "\n";
+        }
+        std::ofstream myfile;
+        myfile.open(WORKDIR "/loplugin.methodcycles.log", std::ios::app | std::ios::out);
+        myfile << output;
+        myfile.close();
+    }
+
+    bool shouldVisitTemplateInstantiations() const { return true; }
+    bool shouldVisitImplicitCode() const { return true; }
+
+    bool VisitCallExpr(CallExpr*);
+    bool VisitFunctionDecl(const FunctionDecl* decl);
+    bool VisitDeclRefExpr(const DeclRefExpr*);
+    bool VisitCXXConstructExpr(const CXXConstructExpr*);
+
+    bool TraverseFunctionDecl(FunctionDecl*);
+    bool TraverseCXXMethodDecl(CXXMethodDecl*);
+    bool TraverseCXXConstructorDecl(CXXConstructorDecl*);
+    bool TraverseCXXConversionDecl(CXXConversionDecl*);
+    bool TraverseCXXDestructorDecl(CXXDestructorDecl*);
+#if CLANG_VERSION >= 50000
+    bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl*);
+#endif
+private:
+    void logCallToRootMethods(const FunctionDecl* functionDeclFrom,
+                              const FunctionDecl* functionDeclTo);
+    void findRoots(const FunctionDecl* functionDecl,
+                   std::unordered_set<const FunctionDecl*>& roots);
+    MyFuncInfo niceName(const FunctionDecl* functionDecl);
+    bool isLocationMine(SourceLocation loc);
+    std::string toString(SourceLocation loc);
+    FunctionDecl const* currentFunctionDecl = nullptr;
+};
+
+MyFuncInfo MethodCycles::niceName(const FunctionDecl* functionDecl)
+{
+    if (functionDecl->getInstantiatedFromMemberFunction())
+        functionDecl = functionDecl->getInstantiatedFromMemberFunction();
+    else if (functionDecl->getClassScopeSpecializationPattern())
+        functionDecl = functionDecl->getClassScopeSpecializationPattern();
+    else if (functionDecl->getTemplateInstantiationPattern())
+        functionDecl = functionDecl->getTemplateInstantiationPattern();
+
+    MyFuncInfo aInfo;
+    if (!isa<CXXConstructorDecl>(functionDecl))
+    {
+        aInfo.returnType = functionDecl->getReturnType().getCanonicalType().getAsString();
+    }
+    else
+    {
+        aInfo.returnType = "";
+    }
+
+    if (auto methodDecl = dyn_cast<CXXMethodDecl>(functionDecl))
+    {
+        const CXXRecordDecl* recordDecl = methodDecl->getParent();
+        aInfo.nameAndParams
+            = recordDecl->getQualifiedNameAsString() + "::" + functionDecl->getNameAsString() + "(";
+    }
+    else
+    {
+        aInfo.nameAndParams = functionDecl->getQualifiedNameAsString() + "(";
+    }
+    bool bFirst = true;
+    for (const ParmVarDecl* pParmVarDecl : compat::parameters(*functionDecl))
+    {
+        if (bFirst)
+            bFirst = false;
+        else
+            aInfo.nameAndParams += ",";
+        aInfo.nameAndParams += pParmVarDecl->getType().getCanonicalType().getAsString();
+    }
+    aInfo.nameAndParams += ")";
+    if (isa<CXXMethodDecl>(functionDecl) && dyn_cast<CXXMethodDecl>(functionDecl)->isConst())
+    {
+        aInfo.nameAndParams += " const";
+    }
+
+    aInfo.sourceLocation = toString(functionDecl->getLocation());
+
+    return aInfo;
+}
+
+std::string MethodCycles::toString(SourceLocation loc)
+{
+    SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc(loc);
+    StringRef name = compiler.getSourceManager().getFilename(expansionLoc);
+    std::string sourceLocation
+        = std::string(name.substr(strlen(SRCDIR) + 1)) + ":"
+          + std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
+    loplugin::normalizeDotDotInFilePath(sourceLocation);
+    return sourceLocation;
+}
+
+bool MethodCycles::isLocationMine(SourceLocation loc)
+{
+    SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc(loc);
+    if (compiler.getSourceManager().isInSystemHeader(expansionLoc))
+        return false;
+    const char* bufferName = compiler.getSourceManager().getPresumedLoc(expansionLoc).getFilename();
+    if (bufferName == NULL)
+        return false;
+    if (loplugin::hasPathnamePrefix(bufferName, WORKDIR "/")
+        || loplugin::hasPathnamePrefix(bufferName, BUILDDIR "/")
+        || loplugin::hasPathnamePrefix(bufferName, SRCDIR "/"))
+        return true; // ok
+    return false;
+}
+
+void MethodCycles::logCallToRootMethods(const FunctionDecl* functionDeclFrom,
+                                        const FunctionDecl* functionDeclTo)
+{
+    if (!functionDeclFrom)
+    {
+        // template magic mostly, but also things called from initialisers
+        return;
+    }
+    functionDeclFrom = functionDeclFrom->getCanonicalDecl();
+    functionDeclTo = functionDeclTo->getCanonicalDecl();
+
+    std::unordered_set<const FunctionDecl*> fromRoots;
+    findRoots(functionDeclFrom, fromRoots);
+    std::unordered_set<const FunctionDecl*> toRoots;
+    findRoots(functionDeclTo, toRoots);
+
+    for (auto const& from : fromRoots)
+        for (auto const& to : toRoots)
+            callMap.insert({ from, to });
+}
+
+void MethodCycles::findRoots(const FunctionDecl* functionDecl,
+                             std::unordered_set<const FunctionDecl*>& roots)
+{
+    bool bCalledSuperMethod = false;
+    if (auto methodDecl = dyn_cast<CXXMethodDecl>(functionDecl))
+    {
+        // For virtual/overriding methods, we need to pretend we called from/to root method(s),
+        // so that they get marked as used.
+        for (auto it = methodDecl->begin_overridden_methods();
+             it != methodDecl->end_overridden_methods(); ++it)
+        {
+            findRoots(*it, roots);
+            bCalledSuperMethod = true;
+        }
+    }
+    if (!bCalledSuperMethod)
+    {
+        while (functionDecl->getTemplateInstantiationPattern())
+            functionDecl = functionDecl->getTemplateInstantiationPattern();
+        if (functionDecl->getLocation().isValid())
+            roots.insert(functionDecl);
+    }
+}
+
+bool MethodCycles::VisitCallExpr(CallExpr* expr)
+{
+    // Note that I don't ignore ANYTHING here, because I want to get calls to my code that result
+    // from template instantiation deep inside the STL and other external code
+
+    FunctionDecl* calleeFunctionDecl = expr->getDirectCallee();
+    if (calleeFunctionDecl == nullptr)
+    {
+        Expr* callee = expr->getCallee()->IgnoreParenImpCasts();
+        DeclRefExpr* dr = dyn_cast<DeclRefExpr>(callee);
+        if (dr)
+        {
+            calleeFunctionDecl = dyn_cast<FunctionDecl>(dr->getDecl());
+            if (calleeFunctionDecl)
+                goto gotfunc;
+        }
+        return true;
+    }
+
+gotfunc:
+
+    if (currentFunctionDecl != calleeFunctionDecl)
+        // ignore recursive calls
+        logCallToRootMethods(currentFunctionDecl, calleeFunctionDecl);
+
+    return true;
+}
+
+bool MethodCycles::VisitCXXConstructExpr(const CXXConstructExpr* constructExpr)
+{
+    // Note that I don't ignore ANYTHING here, because I want to get calls to my code that result
+    // from template instantiation deep inside the STL and other external code
+
+    const CXXConstructorDecl* constructorDecl = constructExpr->getConstructor();
+    constructorDecl = constructorDecl->getCanonicalDecl();
+
+    if (!constructorDecl->getLocation().isValid())
+    {
+        return true;
+    }
+
+    logCallToRootMethods(currentFunctionDecl, constructorDecl);
+
+    return true;
+}
+
+bool MethodCycles::VisitFunctionDecl(const FunctionDecl* functionDecl)
+{
+    const FunctionDecl* canonicalFunctionDecl = functionDecl->getCanonicalDecl();
+    if (functionDecl->isDeleted())
+        return true;
+    // don't care about compiler-generated functions
+    if (functionDecl->isImplicit())
+        return true;
+    if (!canonicalFunctionDecl->getLocation().isValid())
+        return true;
+    // ignore method overrides, since the call will show up as being directed to the root method
+    const CXXMethodDecl* methodDecl = dyn_cast<CXXMethodDecl>(functionDecl);
+    if (methodDecl
+        && (methodDecl->size_overridden_methods() != 0 || methodDecl->hasAttr<OverrideAttr>()))
+        return true;
+    if (!isLocationMine(canonicalFunctionDecl->getLocation()))
+        return true;
+
+    MyFuncInfo funcInfo = niceName(canonicalFunctionDecl);
+    definitionSet.insert(funcInfo);
+    return true;
+}
+
+bool MethodCycles::VisitDeclRefExpr(const DeclRefExpr* declRefExpr)
+{
+    const FunctionDecl* functionDecl = dyn_cast<FunctionDecl>(declRefExpr->getDecl());
+    if (!functionDecl)
+    {
+        return true;
+    }
+    logCallToRootMethods(currentFunctionDecl, functionDecl->getCanonicalDecl());
+
+    return true;
+}
+
+bool MethodCycles::TraverseFunctionDecl(FunctionDecl* f)
+{
+    auto copy = currentFunctionDecl;
+    currentFunctionDecl = f;
+    bool ret = RecursiveASTVisitor::TraverseFunctionDecl(f);
+    currentFunctionDecl = copy;
+    return ret;
+}
+bool MethodCycles::TraverseCXXMethodDecl(CXXMethodDecl* f)
+{
+    auto copy = currentFunctionDecl;
+    currentFunctionDecl = f;
+    bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(f);
+    currentFunctionDecl = copy;
+    return ret;
+}
+bool MethodCycles::TraverseCXXConversionDecl(CXXConversionDecl* f)
+{
+    auto copy = currentFunctionDecl;
+    currentFunctionDecl = f;
+    bool ret = RecursiveASTVisitor::TraverseCXXConversionDecl(f);
+    currentFunctionDecl = copy;
+    return ret;
+}
+#if CLANG_VERSION >= 50000
+bool MethodCycles::TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl* f)
+{
+    auto copy = currentFunctionDecl;
+    currentFunctionDecl = f;
+    bool ret = RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(f);
+    currentFunctionDecl = copy;
+    return ret;
+}
+#endif
+bool MethodCycles::TraverseCXXConstructorDecl(CXXConstructorDecl* f)
+{
+    auto copy = currentFunctionDecl;
+    currentFunctionDecl = f;
+    bool ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(f);
+    currentFunctionDecl = copy;
+    return ret;
+}
+bool MethodCycles::TraverseCXXDestructorDecl(CXXDestructorDecl* f)
+{
+    auto copy = currentFunctionDecl;
+    currentFunctionDecl = f;
+    bool ret = RecursiveASTVisitor::TraverseCXXDestructorDecl(f);
+    currentFunctionDecl = copy;
+    return ret;
+}
+
+loplugin::Plugin::Registration<MethodCycles> X("methodcycles", false);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/compilerplugins/clang/methodcycles.py b/compilerplugins/clang/methodcycles.py
new file mode 100755
index 000000000000..8ff814da8bd5
--- /dev/null
+++ b/compilerplugins/clang/methodcycles.py
@@ -0,0 +1,242 @@
+#!/usr/bin/python
+
+from collections import defaultdict
+import io
+import re
+import subprocess
+import sys
+
+# --------------------------------------------------------------------------------------------
+# globals
+# --------------------------------------------------------------------------------------------
+
+definitionSet = set() # set of method_name
+definitionToSourceLocationMap = dict()
+
+# for the "unused methods" analysis
+callDict = defaultdict(set) # map of from_method_name -> set(method_name)
+
+# clang does not always use exactly the same numbers in the type-parameter vars it generates
+# so I need to substitute them to ensure we can match correctly.
+normalizeTypeParamsRegex = re.compile(r"type-parameter-\d+-\d+")
+def normalizeTypeParams( line ):
+    line = normalizeTypeParamsRegex.sub("type-parameter-?-?", line)
+    # make some of the types a little prettier
+    line = line.replace("std::__debug", "std::")
+    line = line.replace("class ", "")
+    line = line.replace("struct ", "")
+    line = line.replace("_Bool", "bool")
+    return line
+
+# --------------------------------------------------------------------------------------------
+# primary input loop
+# --------------------------------------------------------------------------------------------
+
+cnt = 0
+with io.open("workdir/loplugin.methodcycles.log2", "rb", buffering=1024*1024) as txt:
+    for line in txt:
+        tokens = line.strip().split("\t")
+        if tokens[0] == "definition:":
+            returnType = tokens[1]
+            nameAndParams = tokens[2]
+            sourceLocation = tokens[3]
+            funcInfo = (normalizeTypeParams(returnType) + " " + normalizeTypeParams(nameAndParams)).strip()
+            definitionSet.add(funcInfo)
+            definitionToSourceLocationMap[funcInfo] = sourceLocation
+        elif tokens[0] == "call:":
+            returnTypeFrom = tokens[1]
+            nameAndParamsFrom = tokens[2]
+            returnTypeTo = tokens[3]
+            nameAndParamsTo = tokens[4]
+            caller = (normalizeTypeParams(returnTypeFrom) + " " + normalizeTypeParams(nameAndParamsFrom)).strip()
+            callee = (normalizeTypeParams(returnTypeTo) + " " + normalizeTypeParams(nameAndParamsTo)).strip()
+            callDict[caller].add(callee)
+        else:
+            print( "unknown line: " + line)
+        cnt = cnt + 1
+        #if cnt > 100000: break
+
+# Invert the definitionToSourceLocationMap.
+# If we see more than one method at the same sourceLocation, it's being autogenerated as part of a template
+# and we should just ignore it.
+#sourceLocationToDefinitionMap = {}
+#for k, v in definitionToSourceLocationMap.iteritems():
+#    sourceLocationToDefinitionMap[v] = sourceLocationToDefinitionMap.get(v, [])
+#    sourceLocationToDefinitionMap[v].append(k)
+#for k, definitions in sourceLocationToDefinitionMap.iteritems():
+#    if len(definitions) > 1:
+#        for d in definitions:
+#            definitionSet.remove(d)
+
+# sort the results using a "natural order" so sequences like [item1,item2,item10] sort nicely
+def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
+    return [int(text) if text.isdigit() else text.lower()
+            for text in re.split(_nsre, s)]
+def sort_set_by_natural_key(s):
+    return sorted(s, key=lambda v: natural_sort_key(v[1]))
+
+    
+# --------------------------------------------------------------------------------------------
+#  analysis
+# --------------------------------------------------------------------------------------------
+
+# follow caller-callee chains, removing all methods reachable from a root method
+def remove_reachable(startCaller):
+    worklist = list()
+    worklist.append(startCaller)
+    while len(worklist) > 0:
+        caller = worklist.pop()
+        if not caller in callDict:
+            continue
+        calleeSet = callDict[caller]
+        del callDict[caller]
+        if caller in definitionSet:
+            definitionSet.remove(caller)
+        for c in calleeSet:
+            worklist.append(c)
+
+# look for all the external entry points and remove code called from there
+to_be_removed = set()
+to_be_removed.add("int main(int,char **)")
+# random dynload entrypoints that we don't otherwise find
+to_be_removed.add("bool TestImportOLE2(SvStream &)")
+to_be_removed.add("void SbiRuntime::StepREDIMP()")
+to_be_removed.add("_object * (anonymous namespace)::createUnoStructHelper(_object *,_object *,_object *)");
+for caller in definitionSet:
+    if not caller in definitionToSourceLocationMap:
+        to_be_removed.append(caller)
+        continue
+    location = definitionToSourceLocationMap[caller]
+    if "include/com/" in location \
+      or "include/cppu/" in location \
+      or "include/cppuhelper/" in location \
+      or "include/osl/" in location \
+      or "include/rtl/" in location \
+      or "include/sal/" in location \
+      or "include/salhelper/" in location \
+      or "include/systools/" in location \
+      or "include/typelib/" in location \
+      or "include/uno/" in location \
+      or "workdir/UnpackedTarball/" in location \
+      or "workdir/UnoApiHeadersTarget/" in location \
+      or "workdir/CustomTarget/officecfg/" in location \
+      or "workdir/LexTarget/" in location \
+      or "workdir/CustomTarget/i18npool/localedata/" in location \
+      or "workdir/SdiTarget/" in location \
+      or "/qa/" in location \
+      or "include/test/" in location:
+        to_be_removed.add(caller)
+    # TODO calls to destructors are not mentioned in the AST, so we'll just have to assume they get called,
+    # which is not ideal
+    if "::~" in caller:
+        to_be_removed.add(caller)
+    # dyload entry points for VCL builder
+    if "(VclPtr<vcl::Window> & rRet, VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)" in caller:
+        to_be_removed.add(caller)
+    if "(VclPtr<vcl::Window> &,VclPtr<vcl::Window> &,std::::map<rtl::OString, rtl::OUString, std::less<rtl::OString>, std::allocator<std::pair<const rtl::OString, rtl::OUString> > > &)" in caller:
+        to_be_removed.add(caller)
+# find all the UNO load-by-symbol-name entrypoints
+uno_constructor_entrypoints = set()
+git_grep_process = subprocess.Popen("git grep -h 'constructor=' -- *.component", stdout=subprocess.PIPE, shell=True)
+with git_grep_process.stdout as txt:
+    for line in txt:
+        idx1 = line.find("\"")
+        idx2 = line.find("\"", idx1 + 1)
+        func = line[idx1+1 : idx2]
+        uno_constructor_entrypoints.add(func)
+for caller in callDict:
+    if "(com::sun::star::uno::XComponentContext *,const com::sun::star::uno::Sequence<com::sun::star::uno::Any> &)" in caller:
+        for func in uno_constructor_entrypoints:
+            if func in caller:
+                to_be_removed.add(caller)
+# remove everything reachable from the found entry points
+for caller in to_be_removed:
+    remove_reachable(caller)
+for caller in callDict:
+    callDict[caller] -= to_be_removed
+
+print_tree_recurse_set = set() # protect against cycles
+def print_tree(f, caller, depth):
+    if depth == 0:
+        f.write("\n") # add an empty line before each tree
+        print_tree_recurse_set.clear()
+    # protect against cycles
+    if caller in print_tree_recurse_set:
+        return
+    # when printing out trees, things that are not in the map are things that are reachable,
+    # so we're not interested in them
+    if not caller in callDict:
+        return
+    print_tree_recurse_set.add(caller)
+    f.write("  " * depth + caller + "\n")
+    f.write("  " * depth + definitionToSourceLocationMap[caller] + "\n")
+    calleeSet = callDict[caller]
+    for c in calleeSet:
+        print_tree(f, c, depth+1)
+
+# create a reverse call graph
+inverseCallDict = defaultdict(set) # map of from_method_name -> set(method_name)
+for caller in callDict:
+    for callee in callDict[caller]:
+        inverseCallDict[callee].add(caller)
+
+# find possible roots (ie. entrypoints) by looking for methods that are not called
+def dump_possible_roots():
+    possibleRootList = list()
+    for caller in callDict:
+        if not caller in inverseCallDict and caller in definitionToSourceLocationMap:
+            possibleRootList.append(caller)
+    possibleRootList.sort()
+
+    # print out first 100 trees of caller->callees
+    count = 0
+    with open("compilerplugins/clang/methodcycles.roots", "wt") as f:
+        f.write("callDict size " + str(len(callDict)) + "\n")
+        f.write("possibleRootList size " + str(len(possibleRootList)) + "\n")
+        f.write("\n")
+        for caller in possibleRootList:
+            f.write(caller + "\n")
+            f.write("  " + definitionToSourceLocationMap[caller] + "\n")
+            #print_tree(f, caller, 0)
+            count = count + 1
+            #if count>1000: break
+
+
+# Look for cycles in a directed graph
+# Adapted from:
+# https://codereview.stackexchange.com/questions/86021/check-if-a-directed-graph-contains-a-cycle
+with open("compilerplugins/clang/methodcycles.results", "wt") as f:
+    path = set()
+    visited = set()
+
+    def printPath(path):
+        if len(path) < 2:
+            return
+        # we may have found a cycle, but if the cycle is called from outside the cycle
+        # the code is still in use.
+        for p in path:
+            for caller in inverseCallDict[p]:
+                if not caller in path:
+                    return
+        f.write("found cycle\n")
+        for p in path:
+            f.write("    " + p + "\n")
+            f.write("    " + definitionToSourceLocationMap[p] + "\n")
+            f.write("\n")
+
+    def checkCyclic(vertex):
+        if vertex in visited:
+            return
+        visited.add(vertex)
+        path.add(vertex)
+        if vertex in callDict:
+            for neighbour in callDict[vertex]:
+                if neighbour in path:
+                    printPath(path)
+                    break
+                else:
+                    checkCyclic(neighbour)
+        path.remove(vertex)
+
+    for caller in callDict:
+        checkCyclic(caller)
diff --git a/compilerplugins/clang/methodcycles.results b/compilerplugins/clang/methodcycles.results
new file mode 100644
index 000000000000..403178b25a10
--- /dev/null
+++ b/compilerplugins/clang/methodcycles.results
@@ -0,0 +1,65 @@
+found cycle
+    bool connectivity::OSQLParseTreeIterator::impl_getColumnTableRange(const connectivity::OSQLParseNode *,rtl::OUString &) const
+    include/connectivity/sqliterator.hxx:272
+
+    bool connectivity::OSQLParseTreeIterator::getColumnTableRange(const connectivity::OSQLParseNode *,rtl::OUString &) const
+    include/connectivity/sqliterator.hxx:259
+
+found cycle
+    void (anonymous namespace)::traceValue(_typelib_TypeDescriptionReference *,void *)
+    cppu/source/LogBridge/LogBridge.cxx:132
+
+    void uno_ext_getMapping(_uno_Mapping **,_uno_Environment *,_uno_Environment *)
+    cppu/source/UnsafeBridge/UnsafeBridge.cxx:133
+
+    void LogProbe(bool,void *,void *,_typelib_TypeDescriptionReference *,_typelib_MethodParameter *,int,const _typelib_TypeDescription *,void *,void **,_uno_Any **)
+    cppu/source/LogBridge/LogBridge.cxx:192
+
+found cycle
+    rtl::OUString lcl_dbg_out(SwNodes &)
+    sw/source/core/doc/dbgoutsw.cxx:743
+
+    void lcl_dbg_nodes_inner(rtl::OUString &,SwNodes &,unsigned long &)
+    sw/source/core/doc/dbgoutsw.cxx:694
+
+    const char * dbg_out(SwNodes &)
+    sw/inc/dbgoutsw.hxx:67
+
+found cycle
+    void OutputDevice::DrawPixel(const tools::Polygon &,const Color &)
+    include/vcl/outdev.hxx:710
+
+    void OutputDevice::DrawPixel(const tools::Polygon &,const Color *)
+    include/vcl/outdev.hxx:709
+
+found cycle
+    void SbxVariable::Dump(SvStream &,bool)
+    include/basic/sbxvar.hxx:260
+
+    void SbxObject::Dump(SvStream &,bool)
+    include/basic/sbxobj.hxx:81
+
+    void SbRtl_DumpAllObjects(StarBASIC *,SbxArray &,bool)
+    basic/source/inc/rtlproto.hxx:293
+
+found cycle
+    SbxVariable * SbxArray::FindUserData(unsigned int)
+    include/basic/sbx.hxx:138
+
+    SbxVariable * SbxObject::FindUserData(unsigned int)
+    include/basic/sbxobj.hxx:60
+
+found cycle
+    unsigned long slideshow::internal::hash::operator()(const type-parameter-?-? &) const
+    slideshow/source/inc/tools.hxx:83
+
+    unsigned long com::sun::star::uno::hash_value(const Reference<type-parameter-?-?> &)
+    slideshow/source/inc/tools.hxx:93
+
+found cycle
+    void ScDPResultDimension::DumpState(const ScDPResultMember *,ScDocument *,ScAddress &) const
+    sc/inc/dptabres.hxx:583
+
+    void ScDPResultMember::DumpState(const ScDPResultMember *,ScDocument *,ScAddress &) const
+    sc/inc/dptabres.hxx:413
+
diff --git a/compilerplugins/clang/unusedmethods.cxx b/compilerplugins/clang/unusedmethods.cxx
index a84b7e8d237d..5f998712d3ef 100644
--- a/compilerplugins/clang/unusedmethods.cxx
+++ b/compilerplugins/clang/unusedmethods.cxx
@@ -153,14 +153,19 @@ MyFuncInfo UnusedMethods::niceName(const FunctionDecl* functionDecl)
         aInfo.returnType = "";
     }
 
-    if (const CXXMethodDecl* methodDecl = dyn_cast<CXXMethodDecl>(functionDecl)) {
+    if (auto methodDecl = dyn_cast<CXXMethodDecl>(functionDecl)) {
         const CXXRecordDecl* recordDecl = methodDecl->getParent();
-        aInfo.nameAndParams += recordDecl->getQualifiedNameAsString();
-        aInfo.nameAndParams += "::";
+        aInfo.nameAndParams = recordDecl->getQualifiedNameAsString()
+                + "::"
+                + functionDecl->getNameAsString()
+                + "(";
         if (methodDecl->isVirtual())
             aInfo.virtualness = "virtual";
     }
-    aInfo.nameAndParams += functionDecl->getNameAsString() + "(";
+    else
+    {
+        aInfo.nameAndParams = functionDecl->getQualifiedNameAsString() + "(";
+    }
     bool bFirst = true;
     for (const ParmVarDecl *pParmVarDecl : compat::parameters(*functionDecl)) {
         if (bFirst)
diff --git a/connectivity/source/parse/sqliterator.cxx b/connectivity/source/parse/sqliterator.cxx
index 5ba5c11325be..dcca0fc9db4a 100644
--- a/connectivity/source/parse/sqliterator.cxx
+++ b/connectivity/source/parse/sqliterator.cxx
@@ -780,73 +780,6 @@ void OSQLParseTreeIterator::getColumnRange( const OSQLParseNode* _pColumnRef,
 }
 
 
-bool OSQLParseTreeIterator::getColumnTableRange(const OSQLParseNode* pNode, OUString &rTableRange) const
-{
-    OUString tmp;
-    if(impl_getColumnTableRange(pNode, tmp))
-    {
-        rTableRange = tmp;
-        return true;
-    }
-    else
-        return false;
-}
-
-bool OSQLParseTreeIterator::impl_getColumnTableRange(const OSQLParseNode* pNode, OUString &rTableRange) const
-{
-    // See if all columns belong to one table
-    if (SQL_ISRULE(pNode,column_ref))
-    {
-        OUString aColName, aTableRange;
-        getColumnRange(pNode, aColName, aTableRange);
-        if (aTableRange.isEmpty())   // None found
-        {
-            // Look for the columns in the tables
-            for (auto const& table : *m_pImpl->m_pTables)
-            {
-                if (table.second.is())
-                {
-                    try
-                    {
-                        Reference< XNameAccess > xColumns = table.second->getColumns();
-                        if(xColumns->hasByName(aColName))
-                        {
-                            Reference< XPropertySet > xColumn;
-                            if (xColumns->getByName(aColName) >>= xColumn)
-                            {
-                                OSL_ENSURE(xColumn.is(),"Column isn't a propertyset!");
-                                aTableRange = table.first;
-                                break;
-                            }
-                        }
-                    }
-                    catch(Exception&)
-                    {
-                    }
-                }
-            }
-            if (aTableRange.isEmpty())
-                return false;
-        }
-
-
-        if (rTableRange.isEmpty())
-            rTableRange = aTableRange;
-        else if (rTableRange != aTableRange)
-            return false;
-    }
-    else
-    {
-        for (sal_uInt32 i = 0, ncount = pNode->count(); i < ncount; i++)
-        {
-            if (!getColumnTableRange(pNode->getChild(i), rTableRange))
-                return false;
-        }
-    }
-    return true;
-}
-
-
 void OSQLParseTreeIterator::traverseCreateColumns(const OSQLParseNode* pSelectNode)
 {
     //  aIteratorStatus.Clear();
diff --git a/include/basic/sbx.hxx b/include/basic/sbx.hxx
index 4a51b99fd457..26cfa3b4d05d 100644
--- a/include/basic/sbx.hxx
+++ b/include/basic/sbx.hxx
@@ -135,7 +135,6 @@ public:
     void                 Merge( SbxArray* );
     OUString             GetAlias( sal_uInt16 );
     void                 PutAlias( const OUString&, sal_uInt16 );
-    SbxVariable*         FindUserData( sal_uInt32 nUserData );
     SbxVariable* Find( const OUString&, SbxClassType );
 
     // Additional methods for 32-bit indices
@@ -212,7 +211,6 @@ public:
     SbxCollection();
     SbxCollection( const SbxCollection& );
     SbxCollection& operator=( const SbxCollection& );
-    virtual SbxVariable* FindUserData( sal_uInt32 nUserData ) override;
     virtual SbxVariable* Find( const OUString&, SbxClassType ) override;
     virtual void Clear() override;
 };
diff --git a/include/basic/sbxobj.hxx b/include/basic/sbxobj.hxx
index 86aacecff5c9..7ad0953ae500 100644
--- a/include/basic/sbxobj.hxx
+++ b/include/basic/sbxobj.hxx
@@ -57,7 +57,6 @@ public:
     SbxProperty* GetDfltProperty();
     void SetDfltProperty( const OUString& r );
     // Search for an element
-    virtual SbxVariable* FindUserData( sal_uInt32 nUserData );
     virtual SbxVariable* Find( const OUString&, SbxClassType );
     SbxVariable* FindQualified( const OUString&, SbxClassType );
     // Quick-Call-Interface for Methods
diff --git a/include/connectivity/sqliterator.hxx b/include/connectivity/sqliterator.hxx
index d10bc71ea81d..f62d6790a63f 100644
--- a/include/connectivity/sqliterator.hxx
+++ b/include/connectivity/sqliterator.hxx
@@ -255,9 +255,6 @@ namespace connectivity
                                     OUString &_rColumnName,
                                     OUString& _rTableRange);
 
-        // empty if ambiguous
-        bool getColumnTableRange(const OSQLParseNode* pNode, OUString &rTableRange) const;
-
         // return true when the tableNode is a rule like catalog_name, schema_name or table_name
         static bool isTableNode(const OSQLParseNode* _pTableNode);
 
@@ -268,8 +265,6 @@ namespace connectivity
         ::std::vector< TNodePair >& getJoinConditions() const;
 
     private:
-        // helper to implement getColumnTableRange
-        bool impl_getColumnTableRange(const OSQLParseNode* pNode, OUString &rTableRange) const;
 
         /** traverses the list of table names, and fills _rTables
         */
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index 13ac5a5d05d4..f661bb377636 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -706,8 +706,6 @@ public:
 
     void                        DrawPixel( const Point& rPt );
     void                        DrawPixel( const Point& rPt, const Color& rColor );
-    void                        DrawPixel( const tools::Polygon& rPts, const Color* pColors );
-    void                        DrawPixel( const tools::Polygon& rPts, const Color& rColor );
 
     Color                       GetPixel( const Point& rPt ) const;
     ///@}
diff --git a/sw/inc/dbgoutsw.hxx b/sw/inc/dbgoutsw.hxx
index bc1d93f6c159..b32f410cc36d 100644
--- a/sw/inc/dbgoutsw.hxx
+++ b/sw/inc/dbgoutsw.hxx
@@ -64,7 +64,6 @@ SW_DLLPUBLIC const char * dbg_out(const SwpHints &rHints);
 SW_DLLPUBLIC const char * dbg_out(const SfxPoolItem & rItem);
 SW_DLLPUBLIC const char * dbg_out(const SfxPoolItem * pItem);
 SW_DLLPUBLIC const char * dbg_out(const SfxItemSet & rSet);
-SW_DLLPUBLIC const char * dbg_out(SwNodes & rNodes);
 SW_DLLPUBLIC const char * dbg_out(const SwPosition & rPos);
 SW_DLLPUBLIC const char * dbg_out(const SwPaM & rPam);
 SW_DLLPUBLIC const char * dbg_out(const SwNodeNum & rNum);
diff --git a/sw/source/core/doc/dbgoutsw.cxx b/sw/source/core/doc/dbgoutsw.cxx
index a670fc83ded4..9ffbc68866a0 100644
--- a/sw/source/core/doc/dbgoutsw.cxx
+++ b/sw/source/core/doc/dbgoutsw.cxx
@@ -691,77 +691,6 @@ const char * dbg_out(const SwTextNode * pNode)
         return nullptr;
 }
 
-static void lcl_dbg_nodes_inner(OUString & aStr, SwNodes & rNodes, sal_uLong & nIndex)
-{
-    SwNode * pNode = rNodes[nIndex];
-    SwStartNode * pStartNode = dynamic_cast<SwStartNode *> (pNode);
-
-    SwNode * pEndNode = nullptr;
-    if (pStartNode != nullptr)
-        pEndNode = pStartNode->EndOfSectionNode();
-
-    sal_uLong nCount = rNodes.Count();
-    sal_uLong nStartIndex = nIndex;
-
-    bool bDone = false;
-
-    OUString aTag;
-    if (pNode->IsTableNode())
-        aTag += "table";
-    else if (pNode->IsSectionNode())
-        aTag += "section";
-    else
-        aTag += "nodes";
-
-    aStr += "<";
-    aStr += aTag;
-    aStr += ">";
-
-    while (! bDone)
-    {
-        if (pNode->IsStartNode() && nIndex != nStartIndex)
-            lcl_dbg_nodes_inner(aStr, rNodes, nIndex);
-        else
-        {
-            aStr += lcl_dbg_out(*pNode);
-            aStr += "\n";
-
-            nIndex++;
-        }
-
-        if (pNode == pEndNode || nIndex >= nCount)
-            bDone = true;
-        else
-            pNode = rNodes[nIndex];
-    }
-
-    aStr += "</";
-    aStr += aTag;
-    aStr += ">\n";
-}
-
-static OUString lcl_dbg_out(SwNodes & rNodes)
-{
-    OUString aStr("<nodes-array>");
-
-    sal_uLong nIndex = 0;
-    sal_uLong nCount = rNodes.Count();
-
-    while (nIndex < nCount)
-    {
-        lcl_dbg_nodes_inner(aStr, rNodes, nIndex);
-    }
-
-    aStr += "</nodes-array>\n";
-
-    return aStr;
-}
-
-const char * dbg_out(SwNodes & rNodes)
-{
-    return dbg_out(lcl_dbg_out(rNodes));
-}
-
 static OUString lcl_dbg_out(const SwUndo & rUndo)
 {
     return "[ " + OUString::number(static_cast<int>(rUndo.GetId()))
diff --git a/vcl/source/outdev/pixel.cxx b/vcl/source/outdev/pixel.cxx
index 270a50bf6710..b00835fe96db 100644
--- a/vcl/source/outdev/pixel.cxx
+++ b/vcl/source/outdev/pixel.cxx
@@ -110,71 +110,4 @@ void OutputDevice::DrawPixel( const Point& rPt, const Color& rColor )
         mpAlphaVDev->DrawPixel( rPt );
 }
 
-void OutputDevice::DrawPixel( const tools::Polygon& rPts, const Color* pColors )
-{
-    assert(!is_double_buffered_window());
-
-    if ( !pColors )
-    {
-        DrawPixel( rPts, GetLineColor() );
-    }
-    else
-    {
-        SAL_WARN_IF( !pColors, "vcl", "OutputDevice::DrawPixel: No color array specified" );
-
-        const sal_uInt16 nSize = rPts.GetSize();
-
-        if ( nSize )
-        {
-            if ( mpMetaFile )
-            {
-                for ( sal_uInt16 i = 0; i < nSize; i++ )
-                {
-                    mpMetaFile->AddAction( new MetaPixelAction( rPts[ i ], pColors[ i ] ) );
-                }
-            }
-            if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
-                return;
-
-            if ( mpGraphics || AcquireGraphics() )
-            {
-                if ( mbInitClipRegion )
-                    InitClipRegion();
-
-                if ( mbOutputClipped )
-                    return;
-
-                for ( sal_uInt16 i = 0; i < nSize; i++ )
-                {
-                    const Point aPt( ImplLogicToDevicePixel( rPts[ i ] ) );
-                    mpGraphics->DrawPixel( aPt.X(), aPt.Y(), pColors[ i ], this );
-                }
-            }
-        }
-    }
-
-    if( mpAlphaVDev )
-        mpAlphaVDev->DrawPixel( rPts, pColors );
-}
-
-void OutputDevice::DrawPixel( const tools::Polygon& rPts, const Color& rColor )
-{
-    assert(!is_double_buffered_window());
-
-    if( rColor != COL_TRANSPARENT && ! ImplIsRecordLayout() )
-    {
-        const sal_uInt16 nSize = rPts.GetSize();
-        std::unique_ptr<Color[]> pColArray(new Color[ nSize ]);
-
-        for( sal_uInt16 i = 0; i < nSize; i++ )
-        {
-            pColArray[ i ] = rColor;
-        }
-        DrawPixel( rPts, pColArray.get() );
-    }
-
-    if( mpAlphaVDev )
-        mpAlphaVDev->DrawPixel( rPts, rColor );
-}
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */


More information about the Libreoffice-commits mailing list