compilerplugins/clang/fieldcanbelocal.cxx     |  464 ++++++++++++++++++++++++++
 compilerplugins/clang/fieldcanbelocal.py      |  123 ++++++
 compilerplugins/clang/fieldcanbelocal.results |  123 ++++++
 cui/source/inc/chardlg.hxx                    |    1 
 cui/source/tabpages/chardlg.cxx               |    4 
 include/vcl/tabpage.hxx                       |    2 
 vcl/source/window/tabpage.cxx                 |   10 
 7 files changed, 717 insertions(+), 10 deletions(-)

New commits:
commit 43464d9f31c4aaea07cc84c03d5ce67ce2cdc142
Author:     Noel Grandin <noel.gran...@collabora.co.uk>
AuthorDate: Tue Oct 31 13:53:55 2023 +0200
Commit:     Noel Grandin <noel.gran...@collabora.co.uk>
CommitDate: Wed Nov 1 18:40:07 2023 +0100

    new loplugin:fieldcanbelocal
    
    Change-Id: I33fe8afcbba1d461bee98c92507c878e4e5d41d7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158756
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>

diff --git a/compilerplugins/clang/fieldcanbelocal.cxx 
b/compilerplugins/clang/fieldcanbelocal.cxx
new file mode 100644
index 000000000000..ae1af187befb
--- /dev/null
+++ b/compilerplugins/clang/fieldcanbelocal.cxx
@@ -0,0 +1,464 @@
+/* -*- 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/.
+ */
+
+#if !defined _WIN32 //TODO, #include <sys/file.h>
+
+#include <cassert>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <unordered_set>
+#include <vector>
+#include <algorithm>
+#include <sys/file.h>
+#include <unistd.h>
+
+#include "config_clang.h"
+
+#include "plugin.hxx"
+#include "compat.hxx"
+#include "check.hxx"
+
+#include "clang/AST/ParentMapContext.h"
+
+/**
+ Look for fields on objects that can be local variables.
+ Not a particularly smart plugin, generates a lot of false positives, and 
requires review of the output.
+ Mostly looks for fields that are only accessed within a single method.
+*/
+
+namespace
+{
+struct MyFuncInfo
+{
+    std::string returnType;
+    std::string nameAndParams;
+    std::string sourceLocation;
+};
+
+struct MyFieldInfo
+{
+    std::string parentClass;
+    std::string fieldName;
+    std::string fieldType;
+    std::string sourceLocation;
+};
+
+// try to limit the voluminous output a little
+// if the value is nullptr, that indicates that we touched that field from 
more than one function
+static std::unordered_map<const FieldDecl*, const FunctionDecl*> touchedMap;
+
+class FieldCanBeLocal : public loplugin::FilteringPlugin<FieldCanBeLocal>
+{
+public:
+    explicit FieldCanBeLocal(loplugin::InstantiationData const& data)
+        : FilteringPlugin(data)
+    {
+    }
+
+    virtual void run() override;
+
+    bool shouldVisitTemplateInstantiations() const { return true; }
+    bool shouldVisitImplicitCode() const { return true; }
+
+    bool TraverseCXXConstructorDecl(CXXConstructorDecl*);
+    bool TraverseCXXMethodDecl(CXXMethodDecl*);
+    bool TraverseFunctionDecl(FunctionDecl*);
+
+    bool VisitMemberExpr(const MemberExpr*);
+    bool VisitDeclRefExpr(const DeclRefExpr*);
+    bool VisitInitListExpr(const InitListExpr*);
+    bool VisitCXXConstructorDecl(const CXXConstructorDecl*);
+
+private:
+    MyFieldInfo niceName(const FieldDecl*);
+    MyFuncInfo niceName(const FunctionDecl*);
+    std::string toString(SourceLocation loc);
+    void checkTouched(const FieldDecl* fieldDecl, const FunctionDecl*);
+    bool isSomeKindOfConstant(const Expr* arg);
+
+    RecordDecl* insideMoveOrCopyOrCloneDeclParent = nullptr;
+    RecordDecl* insideStreamOutputOperator = nullptr;
+    // For reasons I do not understand, parentFunctionDecl() is not reliable, 
so
+    // we store the parent function on the way down the AST.
+    FunctionDecl* insideFunctionDecl = nullptr;
+};
+
+void FieldCanBeLocal::run()
+{
+    handler.enableTreeWideAnalysisMode();
+
+    TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
+
+    if (!isUnitTestMode())
+    {
+        // 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;
+        output.reserve(64 * 1024);
+        for (const auto& pair : touchedMap)
+        {
+            if (pair.first->getParent()->isLambda())
+                continue;
+            MyFieldInfo s = niceName(pair.first);
+            output += "definition:\t" + s.parentClass //
+                      + "\t" + s.fieldName //
+                      + "\t" + s.fieldType //
+                      + "\t" + s.sourceLocation //
+                      + "\n";
+            // we have to output a negative, in case, in some other file, it 
is touched only once
+            if (!pair.second)
+                output += "touched:\t" + s.parentClass //
+                          + "\t" + s.fieldName //
+                          + "\tNegative" //
+                          + "\tnowhere.cxx" //
+                          + "\n";
+            else
+            {
+                MyFuncInfo s2 = niceName(pair.second);
+                output += "touched:\t" + s.parentClass //
+                          + "\t" + s.fieldName //
+                          + "\t" + s2.returnType + " " + s2.nameAndParams //
+                          + "\t" + s2.sourceLocation //
+                          + "\n";
+            }
+        }
+        std::ofstream myfile;
+        myfile.open(WORKDIR "/loplugin.fieldcanbelocal.log", std::ios::app | 
std::ios::out);
+        myfile << output;
+        myfile.close();
+    }
+    else
+    {
+        //        for (const MyFieldInfo & s : readFromSet)
+        //            report(
+        //                DiagnosticsEngine::Warning,
+        //                "read %0",
+        //                s.parentRecord->getBeginLoc())
+        //                << s.fieldName;
+    }
+}
+
+MyFieldInfo FieldCanBeLocal::niceName(const FieldDecl* fieldDecl)
+{
+    MyFieldInfo aInfo;
+
+    const RecordDecl* recordDecl = fieldDecl->getParent();
+
+    if (const CXXRecordDecl* cxxRecordDecl = 
dyn_cast<CXXRecordDecl>(recordDecl))
+    {
+        if (cxxRecordDecl->getTemplateInstantiationPattern())
+            cxxRecordDecl = cxxRecordDecl->getTemplateInstantiationPattern();
+        aInfo.parentClass = cxxRecordDecl->getQualifiedNameAsString();
+    }
+    else
+    {
+        aInfo.parentClass = recordDecl->getQualifiedNameAsString();
+    }
+
+    aInfo.fieldName = fieldDecl->getNameAsString();
+    // sometimes the name (if it's an anonymous thing) contains the full path 
of the build folder, which we don't need
+    size_t idx = aInfo.fieldName.find(SRCDIR);
+    if (idx != std::string::npos)
+    {
+        aInfo.fieldName = aInfo.fieldName.replace(idx, strlen(SRCDIR), "");
+    }
+    aInfo.fieldType = fieldDecl->getType().getAsString();
+
+    SourceLocation expansionLoc
+        = 
compiler.getSourceManager().getExpansionLoc(fieldDecl->getLocation());
+    StringRef name = getFilenameOfLocation(expansionLoc);
+    aInfo.sourceLocation
+        = std::string(name.substr(strlen(SRCDIR) + 1)) + ":"
+          + 
std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
+    loplugin::normalizeDotDotInFilePath(aInfo.sourceLocation);
+
+    return aInfo;
+}
+
+MyFuncInfo FieldCanBeLocal::niceName(const FunctionDecl* functionDecl)
+{
+    if (functionDecl->getInstantiatedFromMemberFunction())
+        functionDecl = functionDecl->getInstantiatedFromMemberFunction();
+    else if (functionDecl->getTemplateInstantiationPattern())
+        functionDecl = functionDecl->getTemplateInstantiationPattern();
+
+    MyFuncInfo aInfo;
+    if (!isa<CXXConstructorDecl>(functionDecl))
+    {
+        aInfo.returnType = 
functionDecl->getReturnType().getCanonicalType().getAsString();
+    }
+    else
+    {
+        aInfo.returnType = "";
+    }
+
+    if (isa<CXXMethodDecl>(functionDecl))
+    {
+        const CXXRecordDecl* recordDecl = 
dyn_cast<CXXMethodDecl>(functionDecl)->getParent();
+        aInfo.nameAndParams += recordDecl->getQualifiedNameAsString();
+        aInfo.nameAndParams += "::";
+    }
+    aInfo.nameAndParams += functionDecl->getNameAsString() + "(";
+    bool bFirst = true;
+    for (const ParmVarDecl* pParmVarDecl : functionDecl->parameters())
+    {
+        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 FieldCanBeLocal::toString(SourceLocation loc)
+{
+    SourceLocation expansionLoc = 
compiler.getSourceManager().getExpansionLoc(loc);
+    StringRef name = getFilenameOfLocation(expansionLoc);
+    std::string sourceLocation
+        = std::string(name.substr(strlen(SRCDIR) + 1)) + ":"
+          + 
std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
+    loplugin::normalizeDotDotInFilePath(sourceLocation);
+    return sourceLocation;
+}
+
+bool FieldCanBeLocal::TraverseCXXConstructorDecl(CXXConstructorDecl* 
cxxConstructorDecl)
+{
+    auto copy = insideMoveOrCopyOrCloneDeclParent;
+    if (!ignoreLocation(cxxConstructorDecl->getBeginLoc())
+        && cxxConstructorDecl->isThisDeclarationADefinition())
+    {
+        if (cxxConstructorDecl->isCopyOrMoveConstructor())
+            insideMoveOrCopyOrCloneDeclParent = 
cxxConstructorDecl->getParent();
+    }
+    bool ret = 
RecursiveASTVisitor::TraverseCXXConstructorDecl(cxxConstructorDecl);
+    insideMoveOrCopyOrCloneDeclParent = copy;
+    return ret;
+}
+
+bool FieldCanBeLocal::TraverseCXXMethodDecl(CXXMethodDecl* cxxMethodDecl)
+{
+    auto copy1 = insideMoveOrCopyOrCloneDeclParent;
+    auto copy2 = insideFunctionDecl;
+    if (!ignoreLocation(cxxMethodDecl->getBeginLoc())
+        && cxxMethodDecl->isThisDeclarationADefinition())
+    {
+        if (cxxMethodDecl->isCopyAssignmentOperator() || 
cxxMethodDecl->isMoveAssignmentOperator()
+            || (cxxMethodDecl->getIdentifier()
+                && (cxxMethodDecl->getName().startswith("Clone")
+                    || cxxMethodDecl->getName().startswith("clone")
+                    || cxxMethodDecl->getName().startswith("createClone"))))
+            insideMoveOrCopyOrCloneDeclParent = cxxMethodDecl->getParent();
+        // these are similar in that they tend to simply enumerate all the 
fields of an object without putting
+        // them to some useful purpose
+        auto op = cxxMethodDecl->getOverloadedOperator();
+        if (op == OO_EqualEqual || op == OO_ExclaimEqual)
+            insideMoveOrCopyOrCloneDeclParent = cxxMethodDecl->getParent();
+    }
+    insideFunctionDecl = cxxMethodDecl;
+    bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(cxxMethodDecl);
+    insideMoveOrCopyOrCloneDeclParent = copy1;
+    insideFunctionDecl = copy2;
+    return ret;
+}
+
+bool FieldCanBeLocal::TraverseFunctionDecl(FunctionDecl* functionDecl)
+{
+    auto copy1 = insideStreamOutputOperator;
+    auto copy2 = insideFunctionDecl;
+    auto copy3 = insideMoveOrCopyOrCloneDeclParent;
+    if (functionDecl->getLocation().isValid() && 
!ignoreLocation(functionDecl->getBeginLoc())
+        && functionDecl->isThisDeclarationADefinition())
+    {
+        auto op = functionDecl->getOverloadedOperator();
+        if (op == OO_LessLess && functionDecl->getNumParams() == 2)
+        {
+            QualType qt = functionDecl->getParamDecl(1)->getType();
+            insideStreamOutputOperator
+                = 
qt.getNonReferenceType().getUnqualifiedType()->getAsCXXRecordDecl();
+        }
+        // these are similar in that they tend to simply enumerate all the 
fields of an object without putting
+        // them to some useful purpose
+        if (op == OO_EqualEqual || op == OO_ExclaimEqual)
+        {
+            QualType qt = functionDecl->getParamDecl(1)->getType();
+            insideMoveOrCopyOrCloneDeclParent
+                = 
qt.getNonReferenceType().getUnqualifiedType()->getAsCXXRecordDecl();
+        }
+    }
+    insideFunctionDecl = functionDecl;
+    bool ret = RecursiveASTVisitor::TraverseFunctionDecl(functionDecl);
+    insideStreamOutputOperator = copy1;
+    insideFunctionDecl = copy2;
+    insideMoveOrCopyOrCloneDeclParent = copy3;
+    return ret;
+}
+
+bool FieldCanBeLocal::VisitMemberExpr(const MemberExpr* memberExpr)
+{
+    const ValueDecl* decl = memberExpr->getMemberDecl();
+    const FieldDecl* fieldDecl = dyn_cast<FieldDecl>(decl);
+    if (!fieldDecl)
+    {
+        return true;
+    }
+    fieldDecl = fieldDecl->getCanonicalDecl();
+    if (ignoreLocation(fieldDecl->getBeginLoc()))
+    {
+        return true;
+    }
+    // ignore stuff that forms part of the stable URE interface
+    if 
(isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(fieldDecl->getLocation())))
+    {
+        return true;
+    }
+
+    if (insideMoveOrCopyOrCloneDeclParent || insideStreamOutputOperator)
+    {
+        RecordDecl const* cxxRecordDecl1 = fieldDecl->getParent();
+        // we don't care about reads from a field when inside the copy/move 
constructor/operator= for that field
+        if (cxxRecordDecl1 && (cxxRecordDecl1 == 
insideMoveOrCopyOrCloneDeclParent))
+            return true;
+        // we don't care about reads when the field is being used in an output 
operator, this is normally
+        // debug stuff
+        if (cxxRecordDecl1 && (cxxRecordDecl1 == insideStreamOutputOperator))
+            return true;
+    }
+
+    checkTouched(fieldDecl, insideFunctionDecl);
+
+    return true;
+}
+
+bool FieldCanBeLocal::VisitDeclRefExpr(const DeclRefExpr* declRefExpr)
+{
+    const Decl* decl = declRefExpr->getDecl();
+    const FieldDecl* fieldDecl = dyn_cast<FieldDecl>(decl);
+    if (!fieldDecl)
+    {
+        return true;
+    }
+    fieldDecl = fieldDecl->getCanonicalDecl();
+    if (ignoreLocation(fieldDecl->getBeginLoc()))
+    {
+        return true;
+    }
+    // ignore stuff that forms part of the stable URE interface
+    if 
(isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(fieldDecl->getLocation())))
+    {
+        return true;
+    }
+
+    checkTouched(fieldDecl, insideFunctionDecl);
+
+    return true;
+}
+
+// fields that are assigned via member initialisers do not get visited in 
VisitDeclRef, so
+// have to do it here
+bool FieldCanBeLocal::VisitCXXConstructorDecl(const CXXConstructorDecl* 
cxxConstructorDecl)
+{
+    if (ignoreLocation(cxxConstructorDecl->getBeginLoc()))
+    {
+        return true;
+    }
+    // ignore stuff that forms part of the stable URE interface
+    if (isInUnoIncludeFile(
+            
compiler.getSourceManager().getSpellingLoc(cxxConstructorDecl->getLocation())))
+    {
+        return true;
+    }
+
+    // templates make EvaluateAsInt crash inside clang
+    if (cxxConstructorDecl->isDependentContext())
+        return true;
+
+    // we don't care about writes to a field when inside the copy/move 
constructor/operator= for that field
+    if (insideMoveOrCopyOrCloneDeclParent
+        && cxxConstructorDecl->getParent() == 
insideMoveOrCopyOrCloneDeclParent)
+        return true;
+
+    for (auto it = cxxConstructorDecl->init_begin(); it != 
cxxConstructorDecl->init_end(); ++it)
+    {
+        const CXXCtorInitializer* init = *it;
+        const FieldDecl* fieldDecl = init->getMember();
+        if (!fieldDecl)
+            continue;
+        if (init->getInit() && isSomeKindOfConstant(init->getInit()))
+            checkTouched(fieldDecl, cxxConstructorDecl);
+        else
+            touchedMap[fieldDecl] = nullptr;
+    }
+    return true;
+}
+
+// Fields that are assigned via init-list-expr do not get visited in 
VisitDeclRef, so
+// have to do it here.
+bool FieldCanBeLocal::VisitInitListExpr(const InitListExpr* initListExpr)
+{
+    if (ignoreLocation(initListExpr->getBeginLoc()))
+        return true;
+
+    QualType varType = 
initListExpr->getType().getDesugaredType(compiler.getASTContext());
+    auto recordType = varType->getAs<RecordType>();
+    if (!recordType)
+        return true;
+
+    auto recordDecl = recordType->getDecl();
+    for (auto it = recordDecl->field_begin(); it != recordDecl->field_end(); 
++it)
+    {
+        checkTouched(*it, insideFunctionDecl);
+    }
+
+    return true;
+}
+
+void FieldCanBeLocal::checkTouched(const FieldDecl* fieldDecl, const 
FunctionDecl* functionDecl)
+{
+    auto methodDecl = dyn_cast_or_null<CXXMethodDecl>(functionDecl);
+    if (!methodDecl)
+    {
+        touchedMap[fieldDecl] = nullptr;
+        return;
+    }
+    if (methodDecl->getParent() != fieldDecl->getParent())
+    {
+        touchedMap[fieldDecl] = nullptr;
+        return;
+    }
+    auto it = touchedMap.find(fieldDecl);
+    if (it == touchedMap.end())
+        touchedMap.emplace(fieldDecl, functionDecl);
+    else if (it->second != functionDecl)
+        it->second = nullptr;
+}
+
+bool FieldCanBeLocal::isSomeKindOfConstant(const Expr* arg)
+{
+    assert(arg);
+    if (arg->isValueDependent())
+        return false;
+    return arg->isCXX11ConstantExpr(compiler.getASTContext());
+}
+
+loplugin::Plugin::Registration<FieldCanBeLocal> X("fieldcanbelocal", false);
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/compilerplugins/clang/fieldcanbelocal.py 
b/compilerplugins/clang/fieldcanbelocal.py
new file mode 100755
index 000000000000..3266574445c5
--- /dev/null
+++ b/compilerplugins/clang/fieldcanbelocal.py
@@ -0,0 +1,123 @@
+#!/usr/bin/python3
+
+import re
+import io
+
+definitionSet = set()
+definitionToSourceLocationMap = dict()
+definitionToTypeMap = dict()
+touchedMap = dict()
+excludeSet = set()
+sourceLocationSet = set()
+
+# 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 ):
+    return normalizeTypeParamsRegex.sub("type-parameter-?-?", line)
+
+
+with io.open("workdir/loplugin.fieldcanbelocal.log", "r", buffering=1024*1024) 
as txt:
+    for line in txt:
+        tokens = line.strip().split("\t")
+        if tokens[0] == "definition:":
+            fieldInfo = (normalizeTypeParams(tokens[1]), tokens[2])
+            fieldType = tokens[3]
+            srcLoc = tokens[4]
+            # ignore external source code
+            if srcLoc.startswith("external/"):
+                continue
+            # ignore build folder
+            if srcLoc.startswith("workdir/"):
+                continue
+            definitionSet.add(fieldInfo)
+            definitionToTypeMap[fieldInfo] = fieldType
+            definitionToSourceLocationMap[fieldInfo] = srcLoc
+        elif tokens[0] == "touched:":
+            fieldInfo = (normalizeTypeParams(tokens[1]), tokens[2])
+            touchedByFunction = normalizeTypeParams(tokens[3])
+            touchedByFunctionSrcLoc = tokens[4]
+            if fieldInfo in excludeSet:
+                continue
+            if touchedByFunction == "Negative":
+                excludeSet.add(fieldInfo)
+                if fieldInfo in touchedMap:
+                    touchedMap.pop(fieldInfo)
+            elif fieldInfo in touchedMap:
+                if touchedMap[fieldInfo] != touchedByFunction:
+                    excludeSet.add(fieldInfo)
+                    touchedMap.pop(fieldInfo)
+            else:
+                touchedMap[fieldInfo] = touchedByFunction
+        else:
+            print( "unknown line: " + line)
+
+outputSet = set()
+for d in definitionSet:
+    if not d in touchedMap:
+        continue
+    fieldType = definitionToTypeMap[d]
+    # ignore some types that are known false+
+    if (fieldType.startswith("std::unique_ptr<")
+        or fieldType == "std::mutex"
+        or "Mutex" in fieldType
+        or "union" in fieldType
+        or "anonymous namespace" in fieldType
+        or "unnamed struct" in fieldType):
+        continue
+    # ignore some field names that are known false+
+    if (d[1] == "mbDisposing"
+        or d[1] == "bInDispose"
+        or d[1] == "m_bDisposing"
+        or d[1].startswith("m_bIn")):
+        continue
+    srcLoc = definitionToSourceLocationMap[d]
+    # ignore some types in the system libraries we somehow pick up
+    if srcLoc.startswith(".") or srcLoc.startswith("/") or 
srcLoc.startswith("lib/"):
+        continue
+    # part of the URE
+    if srcLoc.startswith("include/cppuhelper/"):
+        continue
+    # on-disk structures
+    if srcLoc.startswith("hwpfilter/"):
+        continue
+    if srcLoc.startswith("include/osl/"):
+        continue
+    if srcLoc.startswith("include/sal/"):
+        continue
+    if srcLoc.startswith("sw/source/filter/ww8/ww8struc.hxx"):
+        continue
+    if srcLoc.startswith("sd/source/filter/ppt/ppt97animations.hxx"):
+        continue
+    if srcLoc.startswith("lotuswordpro/"):
+        continue
+    if srcLoc.startswith("include/filter/msfilter/svdfppt.hxx"):
+        continue
+    if srcLoc.startswith("filter/source/graphicfilter/icgm/chart.hxx"):
+        continue
+    # most of this code is only compiled on windows, so we don't have decent 
results
+    if srcLoc.startswith("include/svl/svdde.hxx"):
+        continue
+    touchedByFunction = touchedMap[d]
+    outputSet.add((d[0] + " " + d[1] + " " + definitionToTypeMap[d], srcLoc, 
touchedByFunction))
+
+# 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)]
+# sort by both the source-line and the datatype, so the output file ordering 
is stable
+# when we have multiple items on the same source line
+def v_sort_key(v):
+    return natural_sort_key(v[1]) + [v[0]]
+
+# sort results by name and line number
+tmp1list = sorted(outputSet, key=lambda v: v_sort_key(v))
+
+# print out the results
+with open("compilerplugins/clang/fieldcanbelocal.results", "wt") as f:
+    for t in tmp1list:
+        f.write( t[1] + "\n" )
+        f.write( "    " + t[0] + "\n" )
+        f.write( "    " + t[2] + "\n" )
+
+
diff --git a/compilerplugins/clang/fieldcanbelocal.results 
b/compilerplugins/clang/fieldcanbelocal.results
new file mode 100644
index 000000000000..6b5ae55214e1
--- /dev/null
+++ b/compilerplugins/clang/fieldcanbelocal.results
@@ -0,0 +1,123 @@
+chart2/source/inc/CachedDataSequence.hxx:128
+    chart::CachedDataSequence m_nNumberFormatKey sal_Int32
+    void chart::CachedDataSequence::registerProperties()
+connectivity/source/drivers/firebird/ResultSet.hxx:85
+    connectivity::firebird::OResultSet m_statusVector ISC_STATUS_ARRAY
+    unsigned char connectivity::firebird::OResultSet::next()
+connectivity/source/inc/FDatabaseMetaDataResultSet.hxx:114
+    connectivity::ODatabaseMetaDataResultSet m_nFetchSize sal_Int32
+    void connectivity::ODatabaseMetaDataResultSet::construct()
+connectivity/source/inc/FDatabaseMetaDataResultSet.hxx:115
+    connectivity::ODatabaseMetaDataResultSet m_nResultSetType sal_Int32
+    void connectivity::ODatabaseMetaDataResultSet::construct()
+connectivity/source/inc/FDatabaseMetaDataResultSet.hxx:116
+    connectivity::ODatabaseMetaDataResultSet m_nFetchDirection sal_Int32
+    void connectivity::ODatabaseMetaDataResultSet::construct()
+connectivity/source/inc/FDatabaseMetaDataResultSet.hxx:117
+    connectivity::ODatabaseMetaDataResultSet m_nResultSetConcurrency sal_Int32
+    void connectivity::ODatabaseMetaDataResultSet::construct()
+cppuhelper/inc/interfacecontainer4.hxx:267
+    cppuhelper::OInterfaceContainerHelper4::NotifySingleListener m_rEvent 
const EventT &
+    void 
cppuhelper::OInterfaceContainerHelper4::NotifySingleListener::operator()(const 
Reference<type-parameter-?-?> &) const
+cui/source/inc/chardlg.hxx:170
+    SvxCharEffectsPage m_nHtmlMode sal_uInt16
+    void SvxCharEffectsPage::Initialize()
+embeddedobj/source/msole/olecomponent.hxx:73
+    OleComponent m_bWorkaroundActive _Bool
+    _Bool OleComponent::IsWorkaroundActive() const
+include/editeng/adjustitem.hxx:37
+    SvxAdjustItem bLeft _Bool
+    void SvxAdjustItem::SetAdjust(const enum SvxAdjust)
+include/o3tl/enumarray.hxx:130
+    o3tl::enumarray_const_iterator m_buf const EA *
+    const typename type-parameter-?-?::value_type & 
o3tl::enumarray_const_iterator::operator*() const
+include/oox/dump/oledumper.hxx:743
+    oox::dump::VbaDirStreamObject mnCurrOffset sal_Int32
+    void oox::dump::VbaDirStreamObject::implDumpRecordBody()
+include/salhelper/dynload.hxx:82
+    salhelper::ORealDynamicLoader m_refCount sal_uInt32
+     salhelper::ORealDynamicLoader::ORealDynamicLoader(class 
salhelper::ORealDynamicLoader **,const class rtl::OUString &,const class 
rtl::OUString &,void *,void *)
+include/sfx2/msg.hxx:104
+    SfxType createSfxPoolItemFunc std::function<SfxPoolItem *(void)>
+    class std::unique_ptr<class SfxPoolItem> SfxType::CreateItem() const
+include/sfx2/msg.hxx:105
+    SfxType pType const std::type_info *
+    const class std::type_info * SfxType::Type() const
+include/sfx2/msg.hxx:117
+    SfxType0 pType const std::type_info *
+    const class std::type_info * SfxType0::Type() const
+include/test/sheet/xdatapilottable.hxx:31
+    apitest::XDataPilotTable xCellForChange 
css::uno::Reference<css::table::XCell>
+    void apitest::XDataPilotTable::testRefresh()
+include/test/sheet/xdatapilottable.hxx:32
+    apitest::XDataPilotTable xCellForCheck 
css::uno::Reference<css::table::XCell>
+    void apitest::XDataPilotTable::testRefresh()
+include/vcl/tabpage.hxx:43
+    TabPage mbHasHoriBar _Bool
+    void TabPage::ImplInit(class vcl::Window *,long)
+include/vcl/tabpage.hxx:44
+    TabPage mbHasVertBar _Bool
+    void TabPage::ImplInit(class vcl::Window *,long)
+jvmfwk/plugins/sunmajor/pluginlib/util.cxx:205
+    jfw_plugin::(anonymous namespace)::FileHandleReader m_aBuffer char[1024]
+    enum jfw_plugin::(anonymous namespace)::FileHandleReader::Result 
jfw_plugin::(anonymous namespace)::FileHandleReader::readLine(class 
rtl::OString *)
+sc/source/ui/inc/datastream.hxx:104
+    sc::DataStream mnSettings sal_uInt32
+    void sc::DataStream::Decode(const class rtl::OUString &,const class 
ScRange &,int,enum sc::DataStream::MoveType,const unsigned int)
+sc/source/ui/inc/tabvwsh.hxx:99
+    ScTabViewShell eFormObjKind SdrObjKind
+    void ScTabViewShell::ExecDraw(class SfxRequest &)
+sc/source/ui/inc/tabvwsh.hxx:144
+    ScTabViewShell bActiveEditSh _Bool
+    void ScTabViewShell::SetEditShell(class EditView *,_Bool)
+sd/source/filter/html/htmlex.hxx:59
+    HtmlExport mnPagesWritten sal_uInt16
+    void HtmlExport::ExportSingleDocument()
+sfx2/source/appl/lnkbase2.cxx:82
+    sfx2::(anonymous namespace)::ImplDdeItem aData DdeData
+    class DdeData * sfx2::(anonymous namespace)::ImplDdeItem::Get(enum 
SotClipboardFormatId)
+sfx2/source/appl/lnkbase2.cxx:83
+    sfx2::(anonymous namespace)::ImplDdeItem aSeq Sequence<sal_Int8>
+    class DdeData * sfx2::(anonymous namespace)::ImplDdeItem::Get(enum 
SotClipboardFormatId)
+svl/source/misc/inethist.cxx:45
+    INetURLHistory_Impl::head_entry m_nMagic sal_uInt32
+    void INetURLHistory_Impl::head_entry::initialize()
+sw/inc/viewopt.hxx:50
+    ViewOptFlags1 bRef _Bool
+     ViewOptFlags1::ViewOptFlags1()
+sw/source/core/text/txtdrop.cxx:702
+    SwDropCapCache m_aFactor sal_uInt16[10]
+    void SwDropCapCache::CalcFontSize(class SwDropPortion *,class 
SwTextFormatInfo &)
+sw/source/core/text/txtdrop.cxx:704
+    SwDropCapCache m_aDescent short[10]
+    void SwDropCapCache::CalcFontSize(class SwDropPortion *,class 
SwTextFormatInfo &)
+sw/source/filter/ww8/ww8par.hxx:663
+    WW8FormulaControl mfUnknown sal_uInt8
+     WW8FormulaControl::WW8FormulaControl(class rtl::OUString,class 
SwWW8ImplReader &)
+sw/source/filter/ww8/ww8par.hxx:1067
+    WW8TabBandDesc bCantSplit90 _Bool
+     WW8TabBandDesc::WW8TabBandDesc()
+sw/source/filter/ww8/ww8scan.hxx:440
+    WW8PLCFx_PCDAttrs m_aShortSprm SVBT32
+    void WW8PLCFx_PCDAttrs::GetSprms(struct WW8PLCFxDesc *)
+sw/source/uibase/inc/fldmgr.hxx:112
+    SwFieldMgr m_nCurFormat sal_uInt32
+    class SwField * SwFieldMgr::GetCurField()
+unoidl/source/unoidlprovider.cxx:89
+    unoidl::detail::(anonymous namespace)::Memory16 byte unsigned char[2]
+    unsigned short unoidl::detail::(anonymous 
namespace)::Memory16::getUnsigned16() const
+vcl/inc/sft.hxx:178
+    vcl::TTGlobalFontInfo_ fsSelection sal_uInt16
+     vcl::TTGlobalFontInfo_::TTGlobalFontInfo_()
+vcl/inc/unx/sessioninhibitor.hxx:46
+    SessionManagerInhibitor mbDPMSWasEnabled BOOL
+    void SessionManagerInhibitor::inhibitDPMS(_Bool,struct _XDisplay *)
+vcl/inc/unx/sessioninhibitor.hxx:47
+    SessionManagerInhibitor mnDPMSStandbyTimeout CARD16
+    void SessionManagerInhibitor::inhibitDPMS(_Bool,struct _XDisplay *)
+vcl/inc/unx/sessioninhibitor.hxx:48
+    SessionManagerInhibitor mnDPMSSuspendTimeout CARD16
+    void SessionManagerInhibitor::inhibitDPMS(_Bool,struct _XDisplay *)
+vcl/inc/unx/sessioninhibitor.hxx:49
+    SessionManagerInhibitor mnDPMSOffTimeout CARD16
+    void SessionManagerInhibitor::inhibitDPMS(_Bool,struct _XDisplay *)
diff --git a/cui/source/inc/chardlg.hxx b/cui/source/inc/chardlg.hxx
index b1fd167d6a72..6d6bf958c2e2 100644
--- a/cui/source/inc/chardlg.hxx
+++ b/cui/source/inc/chardlg.hxx
@@ -167,7 +167,6 @@ private:
     bool                       m_bNewFontColor;
     bool                       m_bEnableNoneFontColor;
     Color                      m_aOrigFontColor;
-    sal_uInt16                 m_nHtmlMode;
 
     weld::TriStateEnabled m_aOutlineState;
     weld::TriStateEnabled m_aShadowState;
diff --git a/cui/source/tabpages/chardlg.cxx b/cui/source/tabpages/chardlg.cxx
index d8ea76e9a9d4..e851816c1787 100644
--- a/cui/source/tabpages/chardlg.cxx
+++ b/cui/source/tabpages/chardlg.cxx
@@ -1368,8 +1368,8 @@ void SvxCharEffectsPage::Initialize()
     }
     if (pHtmlModeItem)
     {
-        m_nHtmlMode = pHtmlModeItem->GetValue();
-        if ( ( m_nHtmlMode & HTMLMODE_ON ) == HTMLMODE_ON )
+        sal_uInt16 nHtmlMode = pHtmlModeItem->GetValue();
+        if ( ( nHtmlMode & HTMLMODE_ON ) == HTMLMODE_ON )
         {
             //!!! hide some controls please
         }
diff --git a/include/vcl/tabpage.hxx b/include/vcl/tabpage.hxx
index e06c4c1a7750..79e0b5a3c201 100644
--- a/include/vcl/tabpage.hxx
+++ b/include/vcl/tabpage.hxx
@@ -40,8 +40,6 @@ private:
     VclPtr<ScrollBar>    m_pVScroll;
     VclPtr<ScrollBar>    m_pHScroll;
     Size                 maScrollArea;
-    bool                 mbHasHoriBar;
-    bool                 mbHasVertBar;
     Point                mnScrollPos;
     tools::Long          mnScrWidth;
 
diff --git a/vcl/source/window/tabpage.cxx b/vcl/source/window/tabpage.cxx
index 90abeaa25968..612b17d2729e 100644
--- a/vcl/source/window/tabpage.cxx
+++ b/vcl/source/window/tabpage.cxx
@@ -32,8 +32,8 @@ void TabPage::ImplInit( vcl::Window* pParent, WinBits nStyle )
 
     Window::ImplInit( pParent, nStyle, nullptr );
 
-    mbHasHoriBar = false;
-    mbHasVertBar = false;
+    bool bHasHoriBar = false;
+    bool bHasVertBar = false;
 
     Link<ScrollBar*,void> aLink( LINK( this, TabPage, ScrollBarHdl ) );
 
@@ -41,21 +41,21 @@ void TabPage::ImplInit( vcl::Window* pParent, WinBits 
nStyle )
     {
         if ( nStyle & WB_AUTOHSCROLL )
         {
-            mbHasHoriBar = true;
+            bHasHoriBar = true;
             m_pHScroll.set(VclPtr<ScrollBar>::Create(this, (WB_HSCROLL | 
WB_DRAG)));
             m_pHScroll->Show();
             m_pHScroll->SetScrollHdl(aLink);
         }
         if ( nStyle &  WB_AUTOVSCROLL )
         {
-            mbHasVertBar = true;
+            bHasVertBar = true;
             m_pVScroll.set(VclPtr<ScrollBar>::Create(this, (WB_VSCROLL | 
WB_DRAG)));
             m_pVScroll->Show();
             m_pVScroll->SetScrollHdl(aLink);
         }
     }
 
-    if ( mbHasHoriBar || mbHasVertBar )
+    if ( bHasHoriBar || bHasVertBar )
     {
         SetStyle( GetStyle() | WB_CLIPCHILDREN );
     }

Reply via email to