[Libreoffice-commits] core.git: compilerplugins/clang extensions/source fpicker/source sd/source sw/source

Stephan Bergmann (via logerrit) logerrit at kemper.freedesktop.org
Sun Jun 7 18:00:38 UTC 2020


 compilerplugins/clang/elidestringvar.cxx            |  431 ++++++++++++++++++++
 compilerplugins/clang/sharedvisitor/dummyplugin.hxx |    1 
 extensions/source/propctrlr/selectlabeldialog.cxx   |    7 
 fpicker/source/office/foldertree.cxx                |    3 
 sd/source/ui/dlg/sdtreelb.cxx                       |    4 
 sw/source/ui/fldui/changedb.cxx                     |    3 
 sw/source/uibase/dbui/dbtree.cxx                    |    3 
 7 files changed, 438 insertions(+), 14 deletions(-)

New commits:
commit 7a3736f908c0ae207567603c61ce0f617339bac0
Author:     Stephan Bergmann <sbergman at redhat.com>
AuthorDate: Sun Jun 7 18:12:15 2020 +0200
Commit:     Stephan Bergmann <sbergman at redhat.com>
CommitDate: Sun Jun 7 20:00:02 2020 +0200

    New loplugin:elidestringvar
    
    Quoting compilerplugins/clang/elidestringvar.cxx (and see there for a
    rationale):  "Find cases where a variable of a string type (at least for now,
    only OUString) is initialized with a literal value (incl. as an empty string)
    and used only once."
    
    (This commit includes fixes that become necessary in code changed after the
    preceding "Upcoming loplugin:elidestringvar" commits.)
    
    As a follow-up TODO, uses of the
    
      explicit OUString( sal_Unicode value )
    
    ctor where value is char or char16_t literal should be detected too, so that
    e.g. one_quote would have been treated like two_quote in
    4b85108773f9851f358a4daa8869eeadc638d103 "Upcoming loplugin:elidestringvar: sc"
    
    > --- a/sc/source/core/tool/compiler.cxx
    > +++ b/sc/source/core/tool/compiler.cxx
    > @@ -1902,9 +1902,8 @@ void ScCompiler::CheckTabQuotes( OUString& rString,
    >              if( bNeedsQuote )
    >              {
    >                  const OUString one_quote('\'');
    > -                const OUString two_quote("''");
    >                  // escape embedded quotes
    > -                rString = rString.replaceAll( one_quote, two_quote );
    > +                rString = rString.replaceAll( one_quote, "''" );
    >              }
    >              break;
    >      }
    
    Change-Id: I7b74f1735a07540e3d789df1d14c84081c824b6c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95696
    Tested-by: Jenkins
    Reviewed-by: Stephan Bergmann <sbergman at redhat.com>

diff --git a/compilerplugins/clang/elidestringvar.cxx b/compilerplugins/clang/elidestringvar.cxx
new file mode 100644
index 000000000000..79aa75b2da8f
--- /dev/null
+++ b/compilerplugins/clang/elidestringvar.cxx
@@ -0,0 +1,431 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef LO_CLANG_SHARED_PLUGINS
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+
+#include "check.hxx"
+#include "compat.hxx"
+#include "plugin.hxx"
+
+// Find cases where a variable of a string type (at least for now, only OUString) is initialized
+// with a literal value (incl. as an empty string) and used only once.  Conservatively this only
+// covers local non-static variables that are not defined outside of the loop (if any) in which they
+// are used, as other cases may deliberately use the variable for performance (or even correctness,
+// if addresses are taken and compared) reasons.
+//
+// For one, the historically heavy syntax for such uses of string literals
+// (RTL_CONSTASCII_USTRINGPARAM etc.) probably explains many of these redundant variables, wich can
+// now be considered cargo-cult baggage.  For another, some of those variables are used as arguments
+// to functions which also have more efficient overloads directly taking string literals.  And for
+// yet another, some cases with default-initialized variables turned out to be effectively unused
+// code that could be removed completely (d073cca5f7c04de3e1bcedda334d864e98ac7835 "Clean up dead
+// code", 91345e7dde6100496a7c9e815b72b2821ae82bc2 "Clean up dead code",
+// 868b0763ac47f765cb48c277897274a595b831d0 "Upcoming loplugin:elidestringvar: dbaccess" in
+// dbaccess/source/ui/app/AppController.cxx, bde0aac4ccf7b830b5ef21d5b9e75e62aee6aaf9 "Clean up dead
+// code", 354aefec42de856b4ab6201ada54a3a3c630b4bd "Upcoming loplugin:elidestringvar: cui" in
+// cui/source/dialogs/SpellDialog.cxx).
+
+namespace
+{
+class ElideStringVar : public loplugin::FilteringPlugin<ElideStringVar>
+{
+public:
+    explicit ElideStringVar(loplugin::InstantiationData const& data)
+        : FilteringPlugin(data)
+    {
+    }
+
+    bool preRun() override { return compiler.getLangOpts().CPlusPlus; }
+
+    void postRun() override
+    {
+        for (auto const& var : vars_)
+        {
+            if (!var.second.singleUse || *var.second.singleUse == nullptr)
+            {
+                continue;
+            }
+            if (containsPreprocessingConditionalInclusion(SourceRange(
+                    compat::getBeginLoc(var.first), compat::getEndLoc(*var.second.singleUse))))
+            {
+                // This is not perfect, as additional uses can be hidden in conditional blocks that
+                // only start after the (would-be) single use (as was the case in
+                // 3bc5057f9689e024957cfa898a221ee2c4c4afe7 "Upcoming loplugin:elidestringvar:
+                // testtools" when built with --enable-debug, but where also fixing the hidden
+                // additional use was trivial).  If this ever becomes a real problem, we can extend
+                // the above check to cover more of the current function body's remainder.
+                continue;
+            }
+            report(DiagnosticsEngine::Warning,
+                   "replace single use of literal OUString variable with the literal",
+                   (*var.second.singleUse)->getExprLoc())
+                << (*var.second.singleUse)->getSourceRange();
+            report(DiagnosticsEngine::Note, "literal OUString variable defined here",
+                   var.first->getLocation())
+                << var.first->getSourceRange();
+        }
+    }
+
+    bool VisitVarDecl(VarDecl const* decl)
+    {
+        if (ignoreLocation(decl))
+        {
+            return true;
+        }
+        if (!decl->isThisDeclarationADefinition())
+        {
+            return true;
+        }
+        if (isa<ParmVarDecl>(decl))
+        {
+            return true;
+        }
+        if (decl->getStorageDuration() != SD_Automatic)
+        {
+            return true;
+        }
+        if (!loplugin::TypeCheck(decl->getType())
+                 .Class("OUString")
+                 .Namespace("rtl")
+                 .GlobalNamespace())
+        {
+            return true;
+        }
+        if (!decl->hasInit())
+        {
+            return true;
+        }
+        auto const e1 = dyn_cast<CXXConstructExpr>(decl->getInit()->IgnoreParenImpCasts());
+        if (e1 == nullptr)
+        {
+            return true;
+        }
+        if (!loplugin::TypeCheck(e1->getType())
+                 .Class("OUString")
+                 .Namespace("rtl")
+                 .GlobalNamespace())
+        {
+            return true;
+        }
+        switch (e1->getNumArgs())
+        {
+            case 0:
+                break;
+            case 2:
+            {
+                auto const e2 = e1->getArg(1);
+                if (!(isa<CXXDefaultArgExpr>(e2)
+                      && loplugin::TypeCheck(e2->getType())
+                             .Struct("Dummy")
+                             .Namespace("libreoffice_internal")
+                             .Namespace("rtl")
+                             .GlobalNamespace()))
+                {
+                    return true;
+                }
+                break;
+            }
+            default:
+                return true;
+        }
+        auto const ok = vars_.emplace(decl, Data(getInnermostLoop()));
+        assert(ok.second);
+        (void)ok;
+        return true;
+    }
+
+    bool VisitDeclRefExpr(DeclRefExpr const* expr)
+    {
+        if (ignoreLocation(expr))
+        {
+            return true;
+        }
+        auto const var = dyn_cast<VarDecl>(expr->getDecl());
+        if (var == nullptr)
+        {
+            return true;
+        }
+        auto const i = vars_.find(var);
+        if (i == vars_.end())
+        {
+            return true;
+        }
+        i->second.singleUse
+            = i->second.singleUse || i->second.innermostLoop != getInnermostLoop() ? nullptr : expr;
+        return true;
+    }
+
+    bool VisitMemberExpr(MemberExpr const* expr)
+    {
+        if (ignoreLocation(expr))
+        {
+            return true;
+        }
+        auto const e = dyn_cast<DeclRefExpr>(expr->getBase()->IgnoreParenImpCasts());
+        if (e == nullptr)
+        {
+            return true;
+        }
+        auto const var = dyn_cast<VarDecl>(e->getDecl());
+        if (var == nullptr)
+        {
+            return true;
+        }
+        auto const i = vars_.find(var);
+        if (i == vars_.end())
+        {
+            return true;
+        }
+        i->second.singleUse = nullptr;
+        return true;
+    }
+
+    bool VisitUnaryOperator(UnaryOperator const* expr)
+    {
+        if (ignoreLocation(expr))
+        {
+            return true;
+        }
+        if (expr->getOpcode() != UO_AddrOf)
+        {
+            return true;
+        }
+        auto const e = dyn_cast<DeclRefExpr>(expr->getSubExpr()->IgnoreParenImpCasts());
+        if (e == nullptr)
+        {
+            return true;
+        }
+        auto const var = dyn_cast<VarDecl>(e->getDecl());
+        if (var == nullptr)
+        {
+            return true;
+        }
+        auto const i = vars_.find(var);
+        if (i == vars_.end())
+        {
+            return true;
+        }
+        i->second.singleUse = nullptr;
+        return true;
+    }
+
+    bool VisitCallExpr(CallExpr const* expr)
+    {
+        if (ignoreLocation(expr))
+        {
+            return true;
+        }
+        auto const fun = expr->getDirectCallee();
+        if (fun == nullptr)
+        {
+            return true;
+        }
+        unsigned const n = std::min(fun->getNumParams(), expr->getNumArgs());
+        for (unsigned i = 0; i != n; ++i)
+        {
+            if (!loplugin::TypeCheck(fun->getParamDecl(i)->getType())
+                     .LvalueReference()
+                     .NonConstVolatile())
+            {
+                continue;
+            }
+            auto const e = dyn_cast<DeclRefExpr>(expr->getArg(i)->IgnoreParenImpCasts());
+            if (e == nullptr)
+            {
+                continue;
+            }
+            auto const var = dyn_cast<VarDecl>(e->getDecl());
+            if (var == nullptr)
+            {
+                continue;
+            }
+            auto const j = vars_.find(var);
+            if (j == vars_.end())
+            {
+                continue;
+            }
+            j->second.singleUse = nullptr;
+        }
+        return true;
+    }
+
+    bool VisitCXXConstructExpr(CXXConstructExpr const* expr)
+    {
+        if (ignoreLocation(expr))
+        {
+            return true;
+        }
+        auto const ctor = expr->getConstructor();
+        unsigned const n = std::min(ctor->getNumParams(), expr->getNumArgs());
+        for (unsigned i = 0; i != n; ++i)
+        {
+            if (!loplugin::TypeCheck(ctor->getParamDecl(i)->getType())
+                     .LvalueReference()
+                     .NonConstVolatile())
+            {
+                continue;
+            }
+            auto const e = dyn_cast<DeclRefExpr>(expr->getArg(i)->IgnoreParenImpCasts());
+            if (e == nullptr)
+            {
+                continue;
+            }
+            auto const var = dyn_cast<VarDecl>(e->getDecl());
+            if (var == nullptr)
+            {
+                continue;
+            }
+            auto const j = vars_.find(var);
+            if (j == vars_.end())
+            {
+                continue;
+            }
+            j->second.singleUse = nullptr;
+        }
+        return true;
+    }
+
+    bool TraverseWhileStmt(WhileStmt* stmt)
+    {
+        bool ret = true;
+        if (PreTraverseWhileStmt(stmt))
+        {
+            ret = FilteringPlugin::TraverseWhileStmt(stmt);
+            PostTraverseWhileStmt(stmt, ret);
+        }
+        return ret;
+    }
+
+    bool PreTraverseWhileStmt(WhileStmt* stmt)
+    {
+        innermostLoop_.push(stmt);
+        return true;
+    }
+
+    bool PostTraverseWhileStmt(WhileStmt* stmt, bool)
+    {
+        assert(!innermostLoop_.empty());
+        assert(innermostLoop_.top() == stmt);
+        innermostLoop_.pop();
+        return true;
+    }
+
+    bool TraverseDoStmt(DoStmt* stmt)
+    {
+        bool ret = true;
+        if (PreTraverseDoStmt(stmt))
+        {
+            ret = FilteringPlugin::TraverseDoStmt(stmt);
+            PostTraverseDoStmt(stmt, ret);
+        }
+        return ret;
+    }
+
+    bool PreTraverseDoStmt(DoStmt* stmt)
+    {
+        innermostLoop_.push(stmt);
+        return true;
+    }
+
+    bool PostTraverseDoStmt(DoStmt* stmt, bool)
+    {
+        assert(!innermostLoop_.empty());
+        assert(innermostLoop_.top() == stmt);
+        innermostLoop_.pop();
+        return true;
+    }
+
+    bool TraverseForStmt(ForStmt* stmt)
+    {
+        bool ret = true;
+        if (PreTraverseForStmt(stmt))
+        {
+            ret = FilteringPlugin::TraverseForStmt(stmt);
+            PostTraverseForStmt(stmt, ret);
+        }
+        return ret;
+    }
+
+    bool PreTraverseForStmt(ForStmt* stmt)
+    {
+        innermostLoop_.push(stmt);
+        return true;
+    }
+
+    bool PostTraverseForStmt(ForStmt* stmt, bool)
+    {
+        assert(!innermostLoop_.empty());
+        assert(innermostLoop_.top() == stmt);
+        innermostLoop_.pop();
+        return true;
+    }
+
+    bool TraverseCXXForRangeStmt(CXXForRangeStmt* stmt)
+    {
+        bool ret = true;
+        if (PreTraverseCXXForRangeStmt(stmt))
+        {
+            ret = FilteringPlugin::TraverseCXXForRangeStmt(stmt);
+            PostTraverseCXXForRangeStmt(stmt, ret);
+        }
+        return ret;
+    }
+
+    bool PreTraverseCXXForRangeStmt(CXXForRangeStmt* stmt)
+    {
+        innermostLoop_.push(stmt);
+        return true;
+    }
+
+    bool PostTraverseCXXForRangeStmt(CXXForRangeStmt* stmt, bool)
+    {
+        assert(!innermostLoop_.empty());
+        assert(innermostLoop_.top() == stmt);
+        innermostLoop_.pop();
+        return true;
+    }
+
+private:
+    void run() override
+    {
+        if (preRun() && TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()))
+        {
+            postRun();
+        }
+    }
+
+    Stmt const* getInnermostLoop() const
+    {
+        return innermostLoop_.empty() ? nullptr : innermostLoop_.top();
+    }
+
+    struct Data
+    {
+        Data(Stmt const* theInnermostLoop)
+            : innermostLoop(theInnermostLoop)
+        {
+        }
+        Stmt const* innermostLoop;
+        llvm::Optional<Expr const*> singleUse;
+    };
+
+    std::stack<Stmt const*> innermostLoop_;
+    std::map<VarDecl const*, Data> vars_;
+};
+
+loplugin::Plugin::Registration<ElideStringVar> elidestringvar("elidestringvar");
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/compilerplugins/clang/sharedvisitor/dummyplugin.hxx b/compilerplugins/clang/sharedvisitor/dummyplugin.hxx
index 32e0e5e560ee..b52dfaebd238 100644
--- a/compilerplugins/clang/sharedvisitor/dummyplugin.hxx
+++ b/compilerplugins/clang/sharedvisitor/dummyplugin.hxx
@@ -41,6 +41,7 @@ public:
     bool TraverseWhileStmt( WhileStmt* ) { return complain(); }
     bool TraverseDoStmt( DoStmt* ) { return complain(); }
     bool TraverseForStmt( ForStmt* ) { return complain(); }
+    bool TraverseCXXForRangeStmt( CXXForRangeStmt* ) { return complain(); }
     bool TraverseUnaryLNot( UnaryOperator* ) { return complain(); }
     bool TraverseBinLAnd( BinaryOperator* ) { return complain(); }
     bool TraverseBinLOr( BinaryOperator* ) { return complain(); }
diff --git a/extensions/source/propctrlr/selectlabeldialog.cxx b/extensions/source/propctrlr/selectlabeldialog.cxx
index a1ddce7b10b4..bc1d16111c14 100644
--- a/extensions/source/propctrlr/selectlabeldialog.cxx
+++ b/extensions/source/propctrlr/selectlabeldialog.cxx
@@ -104,10 +104,9 @@ namespace pcr
 
             // insert the root
             OUString sRootName(PcrRes(RID_STR_FORMS));
-            OUString aFormImage(RID_EXTBMP_FORMS);
             m_xControlTree->insert(nullptr, -1, &sRootName, nullptr,
                                    nullptr, nullptr, false, m_xScratchIter.get());
-            m_xControlTree->set_image(*m_xScratchIter, aFormImage);
+            m_xControlTree->set_image(*m_xScratchIter, RID_EXTBMP_FORMS);
 
             // build the tree
             m_xInitialSelection.reset();
@@ -179,11 +178,9 @@ namespace pcr
                 Reference< XIndexAccess >  xCont(xAsSet, UNO_QUERY);
                 if (xCont.is() && xCont->getCount())
                 {   // yes -> step down
-                    OUString aFormImage(RID_EXTBMP_FORM);
-
                     m_xControlTree->insert(&rContainerEntry, -1, &sName, nullptr,
                                            nullptr, nullptr, false, m_xScratchIter.get());
-                    m_xControlTree->set_image(*m_xScratchIter, aFormImage);
+                    m_xControlTree->set_image(*m_xScratchIter, RID_EXTBMP_FORM);
                     auto xIter = m_xControlTree->make_iterator(&rContainerEntry);
                     m_xControlTree->iter_nth_child(*xIter, nChildren);
                     sal_Int32 nContChildren = InsertEntries(xCont, *xIter);
diff --git a/fpicker/source/office/foldertree.cxx b/fpicker/source/office/foldertree.cxx
index c2c9c764a742..9e618bd9d0b9 100644
--- a/fpicker/source/office/foldertree.cxx
+++ b/fpicker/source/office/foldertree.cxx
@@ -45,10 +45,9 @@ IMPL_LINK(FolderTree, RequestingChildrenHdl, const weld::TreeIter&, rEntry, bool
 
 void FolderTree::InsertRootEntry(const OUString& rId, const OUString& rRootLabel)
 {
-    OUString sFolderImage(RID_BMP_FOLDER);
     m_xTreeView->insert(nullptr, -1, &rRootLabel, &rId, nullptr, nullptr,
                         true, m_xScratchIter.get());
-    m_xTreeView->set_image(*m_xScratchIter, sFolderImage);
+    m_xTreeView->set_image(*m_xScratchIter, RID_BMP_FOLDER);
     m_xTreeView->set_cursor(*m_xScratchIter);
 }
 
diff --git a/sd/source/ui/dlg/sdtreelb.cxx b/sd/source/ui/dlg/sdtreelb.cxx
index 38772de43da2..7bb5f06e5c6f 100644
--- a/sd/source/ui/dlg/sdtreelb.cxx
+++ b/sd/source/ui/dlg/sdtreelb.cxx
@@ -1126,12 +1126,10 @@ void SdPageObjsTLV::Fill( const SdDrawDocument* pInDoc, SfxMedium* pInMedium,
     m_pMedium = pInMedium;
     m_aDocName = rDocName;
 
-    OUString sImgDoc(BMP_DOC_OPEN);
-
     OUString sId(OUString::number(1));
     // insert document name
     m_xTreeView->insert(nullptr, -1, &m_aDocName, &sId, nullptr, nullptr, true, m_xScratchIter.get());
-    m_xTreeView->set_image(*m_xScratchIter, sImgDoc);
+    m_xTreeView->set_image(*m_xScratchIter, BMP_DOC_OPEN);
 }
 
 /**
diff --git a/sw/source/ui/fldui/changedb.cxx b/sw/source/ui/fldui/changedb.cxx
index 379e0645906d..1200a8554061 100644
--- a/sw/source/ui/fldui/changedb.cxx
+++ b/sw/source/ui/fldui/changedb.cxx
@@ -112,7 +112,6 @@ std::unique_ptr<weld::TreeIter> SwChangeDBDlg::Insert(const OUString& rDBName)
     OUString sUserData = rDBName.getToken(0, DB_DELIM, nIdx);
     sal_Int32 nCommandType = sUserData.toInt32();
 
-    OUString aDBImg(RID_BMP_DB);
     OUString rToInsert = nCommandType ? OUStringLiteral(RID_BMP_DBQUERY) : OUStringLiteral(RID_BMP_DBTABLE);
 
     std::unique_ptr<weld::TreeIter> xIter(m_xUsedDBTLB->make_iterator());
@@ -144,7 +143,7 @@ std::unique_ptr<weld::TreeIter> SwChangeDBDlg::Insert(const OUString& rDBName)
 
     m_xUsedDBTLB->insert(nullptr, -1, &sDBName, nullptr, nullptr, nullptr,
                          false, xIter.get());
-    m_xUsedDBTLB->set_image(*xIter, aDBImg);
+    m_xUsedDBTLB->set_image(*xIter, RID_BMP_DB);
     m_xUsedDBTLB->insert(xIter.get(), -1, &sTableName, &sUserData, nullptr, nullptr,
                          false, xIter.get());
     m_xUsedDBTLB->set_image(*xIter, rToInsert);
diff --git a/sw/source/uibase/dbui/dbtree.cxx b/sw/source/uibase/dbui/dbtree.cxx
index cdebbaaed4e1..480ced08f784 100644
--- a/sw/source/uibase/dbui/dbtree.cxx
+++ b/sw/source/uibase/dbui/dbtree.cxx
@@ -172,9 +172,8 @@ void SwDBTreeList::InitTreeList()
 
 void SwDBTreeList::AddDataSource(const OUString& rSource)
 {
-    OUString aImg(RID_BMP_DB);
     m_xTreeView->insert(nullptr, -1, &rSource, nullptr, nullptr, nullptr, true, m_xScratchIter.get());
-    m_xTreeView->set_image(*m_xScratchIter, aImg);
+    m_xTreeView->set_image(*m_xScratchIter, RID_BMP_DB);
     m_xTreeView->select(*m_xScratchIter);
 }
 


More information about the Libreoffice-commits mailing list