[Libreoffice-commits] core.git: 4 commits - compilerplugins/clang desktop/source extensions/source ucb/source

Stephan Bergmann sbergman at redhat.com
Fri Dec 19 01:04:16 PST 2014


 compilerplugins/clang/literalalternative.cxx |  111 --
 compilerplugins/clang/stringconstant.cxx     | 1096 +++++++++++++++++++++++++++
 desktop/source/lib/init.cxx                  |    2 
 extensions/source/plugin/base/xplugin.cxx    |    6 
 ucb/source/ucp/gio/gio_content.cxx           |    4 
 ucb/source/ucp/gio/gio_provider.cxx          |    2 
 6 files changed, 1103 insertions(+), 118 deletions(-)

New commits:
commit 2a52591bfe10c651c9eba66cb44ed7675f5fba26
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Fri Dec 19 10:03:39 2014 +0100

    Extend loplugin:literalalternative to loplugin:stringconstant
    
    Change-Id: Ie425af19019126b6a15ac03f52e32d186a46db35

diff --git a/compilerplugins/clang/literalalternative.cxx b/compilerplugins/clang/literalalternative.cxx
deleted file mode 100644
index 0d29e0d..0000000
--- a/compilerplugins/clang/literalalternative.cxx
+++ /dev/null
@@ -1,111 +0,0 @@
-/* -*- 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 <string>
-
-#include "plugin.hxx"
-
-// Find those calls of rtl::OUString::equalsIgnoreAsciiCaseAscii and
-// rtl::OUString::equalsIgnoreAsciiCaseAsciiL that could be simplified to call
-// rtl::OUString::equalsIgnoreAsciiCase instead:
-
-namespace {
-
-class LiteralAlternative:
-    public RecursiveASTVisitor<LiteralAlternative>, public loplugin::Plugin
-{
-public:
-    explicit LiteralAlternative(InstantiationData const & data): Plugin(data) {}
-
-    virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
-
-    bool VisitCallExpr(const CallExpr * expr);
-};
-
-bool LiteralAlternative::VisitCallExpr(const CallExpr * expr) {
-    if (ignoreLocation(expr)) {
-        return true;
-    }
-    FunctionDecl const * fdecl = expr->getDirectCallee();
-    if (fdecl == nullptr) {
-        return true;
-    }
-    std::string qname { fdecl->getQualifiedNameAsString() };
-    if (qname == "rtl::OUString::equalsIgnoreAsciiCaseAscii"
-        && fdecl->getNumParams() == 1 && expr->getNumArgs() == 1)
-    {
-        // equalsIgnoreAsciiCaseAscii("foo") -> equalsIngoreAsciiCase("foo"):
-        StringLiteral const * lit
-            = dyn_cast<StringLiteral>(expr->getArg(0)->IgnoreParenImpCasts());
-        if (lit != nullptr) {
-            report(
-                DiagnosticsEngine::Warning,
-                ("rewrite call of rtl::OUString::equalsIgnoreAsciiCaseAscii"
-                 " with string literal argument as call of"
-                 " rtl::OUString::equalsIgnoreAsciiCase"),
-                expr->getExprLoc());
-                //TODO: a better loc (the "equalsIgnoreAsciiCaseAscii" part)?
-        }
-        return true;
-    }
-    if (qname == "rtl::OUString::equalsIgnoreAsciiCaseAsciiL"
-        && fdecl->getNumParams() == 2 && expr->getNumArgs() == 2)
-    {
-        // equalsIgnoreAsciiCaseAsciiL("foo", 3) -> equalsIngoreAsciiCase("foo")
-        // especially also for
-        // equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("foo")), where
-        // RTL_CONSTASCII_STRINGPARAM expands to complicated expressions
-        // involving (&(X)[0] sub-expressions (and it might or might not be
-        // better to handle that at the level of non-expanded macros instead,
-        // but I have not found out how to do that yet anyway):
-        APSInt res;
-        if (expr->getArg(1)->isIntegerConstantExpr(res, compiler.getASTContext())) {
-            Expr const * arg0 = expr->getArg(0)->IgnoreParenImpCasts();
-            StringLiteral const * lit = dyn_cast<StringLiteral>(arg0);
-            bool match = false;
-            if (lit != nullptr) {
-                match = res == lit->getLength();
-            } else {
-                UnaryOperator const * op = dyn_cast<UnaryOperator>(arg0);
-                if (op != nullptr && op->getOpcode() == UO_AddrOf) {
-                    ArraySubscriptExpr const * subs
-                        = dyn_cast<ArraySubscriptExpr>(
-                            op->getSubExpr()->IgnoreParenImpCasts());
-                    if (subs != nullptr) {
-                        lit = dyn_cast<StringLiteral>(
-                            subs->getBase()->IgnoreParenImpCasts());
-                        match = lit != nullptr
-                            && subs->getIdx()->isIntegerConstantExpr(
-                                res, compiler.getASTContext())
-                            && res == 0;
-                    }
-                }
-            }
-            if (match) {
-                report(
-                    DiagnosticsEngine::Warning,
-                    ("rewrite call of"
-                     " rtl::OUString::equalsIgnoreAsciiCaseAsciiL with string"
-                     " literal and matching length arguments as call of"
-                     " rtl::OUString::equalsIgnoreAsciiCase"),
-                    expr->getExprLoc());
-                    //TODO: a better loc (the "equalsIgnoreAsciiCaseAsciiL"
-                    // part)?
-            }
-        }
-        return true;
-    }
-    return true;
-}
-
-loplugin::Plugin::Registration< LiteralAlternative > X("literalalternative");
-
-}
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/compilerplugins/clang/stringconstant.cxx b/compilerplugins/clang/stringconstant.cxx
new file mode 100644
index 0000000..12bf173
--- /dev/null
+++ b/compilerplugins/clang/stringconstant.cxx
@@ -0,0 +1,1096 @@
+/* -*- 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 <limits>
+#include <stack>
+#include <string>
+
+#include "plugin.hxx"
+
+// Define a "string constant" to be a constant expression either of type "array
+// of N char" where each array element is a non-NUL ASCII character---except
+// that the last array element may be NUL, or, in some situations, of type char
+// with a ASCII value (including NUL).  Note that the former includes
+// expressions denoting narrow string literals like "foo", and, with toolchains
+// that support constexpr, constexpr variables declared like
+//
+//   constexpr char str[] = "bar";
+//
+// This plugin flags uses of OUString functions with string constant arguments
+// that can be rewritten more directly, like
+//
+//   OUString::createFromAscii("foo")  ->  "foo"
+//
+// and
+//
+//   s.equals(OUString("bar"))  ->  s == "bar"
+
+namespace {
+
+bool isPlainChar(QualType type) {
+    return type->isSpecificBuiltinType(BuiltinType::Char_S)
+        || type->isSpecificBuiltinType(BuiltinType::Char_U);
+}
+
+SourceLocation getMemberLocation(Expr const * expr) {
+    CallExpr const * e1 = dyn_cast<CallExpr>(expr);
+    MemberExpr const * e2 = e1 == nullptr
+        ? nullptr : dyn_cast<MemberExpr>(e1->getCallee());
+    return e2 == nullptr ? expr->getExprLoc()/*TODO*/ : e2->getMemberLoc();
+}
+
+class StringConstant:
+    public RecursiveASTVisitor<StringConstant>, public loplugin::Plugin
+{
+public:
+    explicit StringConstant(InstantiationData const & data): Plugin(data) {}
+
+    void run() override;
+
+    bool TraverseCallExpr(CallExpr * expr);
+
+    bool TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr);
+
+    bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr * expr);
+
+    bool TraverseCXXConstructExpr(CXXConstructExpr * expr);
+
+    bool VisitCallExpr(CallExpr const * expr);
+
+    bool VisitCXXConstructExpr(CXXConstructExpr const * expr);
+
+private:
+    enum class TreatEmpty { DefaultCtor, CheckEmpty, Error };
+
+    enum class ChangeKind { Char, CharLen, SingleChar };
+
+    enum class PassThrough { No, EmptyConstantString, NonEmptyConstantString };
+
+    std::string describeChangeKind(ChangeKind kind);
+
+    bool isStringConstant(
+        Expr const * expr, unsigned * size, bool * nonAscii,
+        bool * embeddedNuls, bool * terminatingNul);
+
+    bool isZero(Expr const * expr);
+
+    void reportChange(
+        Expr const * expr, ChangeKind kind, std::string const & original,
+        std::string const & replacement, PassThrough pass);
+
+    void checkEmpty(
+        CallExpr const * expr, std::string const & qname, TreatEmpty treatEmpty,
+        unsigned size, std::string * replacement);
+
+    void handleChar(
+        CallExpr const * expr, unsigned arg, std::string const & qname,
+        std::string const & replacement, TreatEmpty treatEmpty, bool literal);
+
+    void handleCharLen(
+        CallExpr const * expr, unsigned arg1, unsigned arg2,
+        std::string const & qname, std::string const & replacement,
+        TreatEmpty treatEmpty);
+
+    std::stack<Expr const *> calls_;
+};
+
+void StringConstant::run() {
+    if (compiler.getLangOpts().CPlusPlus
+        && compiler.getPreprocessor().getIdentifierInfo(
+            "LIBO_INTERNAL_ONLY")->hasMacroDefinition())
+            //TODO: some parts of it are useful for external code, too
+    {
+        TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
+    }
+}
+
+bool StringConstant::TraverseCallExpr(CallExpr * expr) {
+    if (!WalkUpFromCallExpr(expr)) {
+        return false;
+    }
+    calls_.push(expr);
+    bool res = true;
+    for (auto * e: expr->children()) {
+        if (!TraverseStmt(e)) {
+            res = false;
+            break;
+        }
+    }
+    calls_.pop();
+    return res;
+}
+
+bool StringConstant::TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr) {
+    if (!WalkUpFromCXXMemberCallExpr(expr)) {
+        return false;
+    }
+    calls_.push(expr);
+    bool res = true;
+    for (auto * e: expr->children()) {
+        if (!TraverseStmt(e)) {
+            res = false;
+            break;
+        }
+    }
+    calls_.pop();
+    return res;
+}
+
+bool StringConstant::TraverseCXXOperatorCallExpr(CXXOperatorCallExpr * expr)
+{
+    if (!WalkUpFromCXXOperatorCallExpr(expr)) {
+        return false;
+    }
+    calls_.push(expr);
+    bool res = true;
+    for (auto * e: expr->children()) {
+        if (!TraverseStmt(e)) {
+            res = false;
+            break;
+        }
+    }
+    calls_.pop();
+    return res;
+}
+
+bool StringConstant::TraverseCXXConstructExpr(CXXConstructExpr * expr) {
+    if (!WalkUpFromCXXConstructExpr(expr)) {
+        return false;
+    }
+    calls_.push(expr);
+    bool res = true;
+    for (auto * e: expr->children()) {
+        if (!TraverseStmt(e)) {
+            res = false;
+            break;
+        }
+    }
+    calls_.pop();
+    return res;
+}
+
+bool StringConstant::VisitCallExpr(CallExpr const * expr) {
+    if (ignoreLocation(expr)) {
+        return true;
+    }
+    FunctionDecl const * fdecl = expr->getDirectCallee();
+    if (fdecl == nullptr) {
+        return true;
+    }
+    std::string qname(fdecl->getQualifiedNameAsString());
+    //TODO: u.compareToAscii("foo") -> u.???("foo")
+    //TODO: u.compareToIgnoreAsciiCaseAscii("foo") -> u.???("foo")
+    if (qname == "rtl::OUString::createFromAscii" && fdecl->getNumParams() == 1)
+    {
+        // OUString::createFromAscii("foo") -> OUString("foo")
+        handleChar(
+            expr, 0, qname, "rtl::OUString constructor",
+            TreatEmpty::DefaultCtor, true);
+        return true;
+    }
+    if (qname == "rtl::OUString::endsWithAsciiL" && fdecl->getNumParams() == 2)
+    {
+        // u.endsWithAsciiL("foo", 3) -> u.endsWith("foo"):
+        handleCharLen(
+            expr, 0, 1, qname, "rtl::OUString::endsWith", TreatEmpty::Error);
+        return true;
+    }
+    if (qname == "rtl::OUString::endsWithIgnoreAsciiCaseAsciiL"
+        && fdecl->getNumParams() == 2)
+    {
+        // u.endsWithIgnoreAsciiCaseAsciiL("foo", 3) ->
+        // u.endsWithIgnoreAsciiCase("foo"):
+        handleCharLen(
+            expr, 0, 1, qname, "rtl::OUString::endsWithIgnoreAsciiCase",
+            TreatEmpty::Error);
+        return true;
+    }
+    if (qname == "rtl::OUString::equalsAscii" && fdecl->getNumParams() == 1) {
+        // u.equalsAscii("foo") -> u == "foo":
+        handleChar(
+            expr, 0, qname, "operator ==", TreatEmpty::CheckEmpty, false);
+        return true;
+    }
+    if (qname == "rtl::OUString::equalsAsciiL" && fdecl->getNumParams() == 2) {
+        // u.equalsAsciiL("foo", 3) -> u == "foo":
+        handleCharLen(expr, 0, 1, qname, "operator ==", TreatEmpty::CheckEmpty);
+        return true;
+    }
+    if (qname == "rtl::OUString::equalsIgnoreAsciiCaseAscii"
+        && fdecl->getNumParams() == 1)
+    {
+        // u.equalsIgnoreAsciiCaseAscii("foo") ->
+        // u.equalsIngoreAsciiCase("foo"):
+        handleChar(
+            expr, 0, qname, "rtl::OUString::equalsIgnoreAsciiCase",
+            TreatEmpty::CheckEmpty, false);
+        return true;
+    }
+    if (qname == "rtl::OUString::equalsIgnoreAsciiCaseAsciiL"
+        && fdecl->getNumParams() == 2)
+    {
+        // u.equalsIgnoreAsciiCaseAsciiL("foo", 3) ->
+        // u.equalsIngoreAsciiCase("foo"):
+        handleCharLen(
+            expr, 0, 1, qname, "rtl::OUString::equalsIgnoreAsciiCase",
+            TreatEmpty::CheckEmpty);
+        return true;
+    }
+    if (qname == "rtl::OUString::indexOfAsciiL" && fdecl->getNumParams() == 3) {
+        assert(expr->getNumArgs() == 3);
+        // u.indexOfAsciiL("foo", 3, i) -> u.indexOf("foo", i):
+        handleCharLen(
+            expr, 0, 1, qname, "rtl::OUString::indexOf", TreatEmpty::Error);
+        return true;
+    }
+    if (qname == "rtl::OUString::lastIndexOfAsciiL"
+        && fdecl->getNumParams() == 2)
+    {
+        // u.lastIndexOfAsciiL("foo", 3) -> u.lastIndexOf("foo"):
+        handleCharLen(
+            expr, 0, 1, qname, "rtl::OUString::lastIndexOf", TreatEmpty::Error);
+        return true;
+    }
+    if (qname == "rtl::OUString::matchAsciiL" && fdecl->getNumParams() == 3) {
+        assert(expr->getNumArgs() == 3);
+        // u.matchAsciiL("foo", 3, i) -> u.match("foo", i):
+        handleCharLen(
+            expr, 0, 1, qname,
+            (isZero(expr->getArg(2))
+             ? std::string("rtl::OUString::startsWith")
+             : std::string("rtl::OUString::match")),
+            TreatEmpty::Error);
+        return true;
+    }
+    if (qname == "rtl::OUString::matchIgnoreAsciiCaseAsciiL"
+        && fdecl->getNumParams() == 3)
+    {
+        assert(expr->getNumArgs() == 3);
+        // u.matchIgnoreAsciiCaseAsciiL("foo", 3, i) ->
+        // u.matchIgnoreAsciiCase("foo", i):
+        handleCharLen(
+            expr, 0, 1, qname,
+            (isZero(expr->getArg(2))
+             ? std::string("rtl::OUString::startsWithIgnoreAsciiCase")
+             : std::string("rtl::OUString::matchIgnoreAsciiCase")),
+            TreatEmpty::Error);
+        return true;
+    }
+    if (qname == "rtl::OUString::reverseCompareToAsciiL"
+        && fdecl->getNumParams() == 2)
+    {
+        // u.reverseCompareToAsciiL("foo", 3) -> u.reverseCompareTo("foo"):
+        handleCharLen(
+            expr, 0, 1, qname, "rtl::OUString::reverseCompareTo",
+            TreatEmpty::Error);
+        return true;
+    }
+    if (qname == "rtl::OUString::equals" && fdecl->getNumParams() == 1) {
+        unsigned n;
+        bool non;
+        bool emb;
+        bool trm;
+        if (!isStringConstant(
+                expr->getArg(0)->IgnoreParenImpCasts(), &n, &non, &emb, &trm))
+        {
+            return true;
+        }
+        if (non) {
+            report(
+                DiagnosticsEngine::Warning,
+                ("call of " + qname
+                 + (" with string constant argument containging non-ASCII"
+                    " characters")),
+                expr->getExprLoc())
+                << expr->getSourceRange();
+        }
+        if (emb) {
+            report(
+                DiagnosticsEngine::Warning,
+                ("call of " + qname
+                 + " with string constant argument containging embedded NULs"),
+                expr->getExprLoc())
+                << expr->getSourceRange();
+        }
+        if (n == 0) {
+            report(
+                DiagnosticsEngine::Warning,
+                ("rewrite call of " + qname
+                 + (" with empty string constant argument as call of"
+                    " rtl::OUString::isEmpty")),
+                expr->getExprLoc())
+                << expr->getSourceRange();
+            return true;
+        }
+    }
+    if (qname == "rtl::operator==" && fdecl->getNumParams() == 2) {
+        for (unsigned i = 0; i != 2; ++i) {
+            unsigned n;
+            bool non;
+            bool emb;
+            bool trm;
+            if (!isStringConstant(
+                    expr->getArg(i)->IgnoreParenImpCasts(), &n, &non, &emb,
+                    &trm))
+            {
+                continue;
+            }
+            if (non) {
+                report(
+                    DiagnosticsEngine::Warning,
+                    ("call of " + qname
+                     + (" with string constant argument containging non-ASCII"
+                        " characters")),
+                    expr->getExprLoc())
+                    << expr->getSourceRange();
+            }
+            if (emb) {
+                report(
+                    DiagnosticsEngine::Warning,
+                    ("call of " + qname
+                     + (" with string constant argument containging embedded"
+                        " NULs")),
+                    expr->getExprLoc())
+                    << expr->getSourceRange();
+            }
+            if (n == 0) {
+                report(
+                    DiagnosticsEngine::Warning,
+                    ("rewrite call of " + qname
+                     + (" with empty string constant argument as call of"
+                        " rtl::OUString::isEmpty")),
+                    expr->getExprLoc())
+                    << expr->getSourceRange();
+            }
+        }
+        return true;
+    }
+    if (qname == "rtl::operator!=" && fdecl->getNumParams() == 2) {
+        for (unsigned i = 0; i != 2; ++i) {
+            unsigned n;
+            bool non;
+            bool emb;
+            bool trm;
+            if (!isStringConstant(
+                    expr->getArg(i)->IgnoreParenImpCasts(), &n, &non, &emb,
+                    &trm))
+            {
+                continue;
+            }
+            if (non) {
+                report(
+                    DiagnosticsEngine::Warning,
+                    ("call of " + qname
+                     + (" with string constant argument containging non-ASCII"
+                        " characters")),
+                    expr->getExprLoc())
+                    << expr->getSourceRange();
+            }
+            if (emb) {
+                report(
+                    DiagnosticsEngine::Warning,
+                    ("call of " + qname
+                     + (" with string constant argument containging embedded"
+                        " NULs")),
+                    expr->getExprLoc())
+                    << expr->getSourceRange();
+            }
+            if (n == 0) {
+                report(
+                    DiagnosticsEngine::Warning,
+                    ("rewrite call of " + qname
+                     + (" with empty string constant argument as call of"
+                        " !rtl::OUString::isEmpty")),
+                    expr->getExprLoc())
+                    << expr->getSourceRange();
+            }
+        }
+        return true;
+    }
+    if (qname == "rtl::OUString::operator=" && fdecl->getNumParams() == 1) {
+        unsigned n;
+        bool non;
+        bool emb;
+        bool trm;
+        if (!isStringConstant(
+                expr->getArg(1)->IgnoreParenImpCasts(), &n, &non, &emb, &trm))
+        {
+            return true;
+        }
+        if (non) {
+            report(
+                DiagnosticsEngine::Warning,
+                ("call of " + qname
+                 + (" with string constant argument containging non-ASCII"
+                    " characters")),
+                expr->getExprLoc())
+                << expr->getSourceRange();
+        }
+        if (emb) {
+            report(
+                DiagnosticsEngine::Warning,
+                ("call of " + qname
+                 + " with string constant argument containging embedded NULs"),
+                expr->getExprLoc())
+                << expr->getSourceRange();
+        }
+        if (n == 0) {
+            report(
+                DiagnosticsEngine::Warning,
+                ("rewrite call of " + qname
+                 + (" with empty string constant argument as call of"
+                    " rtl::OUString::clear")),
+                expr->getExprLoc())
+                << expr->getSourceRange();
+            return true;
+        }
+    }
+    return true;
+}
+
+bool StringConstant::VisitCXXConstructExpr(CXXConstructExpr const * expr) {
+    if (ignoreLocation(expr)) {
+        return true;
+    }
+    std::string qname(
+        expr->getConstructor()->getParent()->getQualifiedNameAsString());
+    if (qname == "rtl::OUString") {
+        ChangeKind kind;
+        PassThrough pass;
+        switch (expr->getConstructor()->getNumParams()) {
+        case 1:
+            {
+                APSInt v;
+                if (!expr->getArg(0)->isIntegerConstantExpr(
+                        v, compiler.getASTContext()))
+                {
+                    return true;
+                }
+                if (v == 0 || v.uge(0x80)) {
+                    return true;
+                }
+                kind = ChangeKind::SingleChar;
+                pass = PassThrough::NonEmptyConstantString;
+                break;
+            }
+        case 2:
+            {
+                unsigned n;
+                bool non;
+                bool emb;
+                bool trm;
+                if (!isStringConstant(
+                        expr->getArg(0)->IgnoreParenImpCasts(), &n, &non, &emb,
+                        &trm))
+                {
+                    return true;
+                }
+                if (non) {
+                    report(
+                        DiagnosticsEngine::Warning,
+                        ("construction of " + qname
+                         + (" with string constant argument containging"
+                            " non-ASCII characters")),
+                        expr->getExprLoc())
+                        << expr->getSourceRange();
+                }
+                if (emb) {
+                    report(
+                        DiagnosticsEngine::Warning,
+                        ("construction of " + qname
+                         + (" with string constant argument containging"
+                            " embedded NULs")),
+                        expr->getExprLoc())
+                        << expr->getSourceRange();
+                }
+                kind = ChangeKind::Char;
+                pass = n == 0
+                    ? PassThrough::EmptyConstantString
+                    : PassThrough::NonEmptyConstantString;
+                break;
+            }
+        default:
+            return true;
+        }
+        if (!calls_.empty()) {
+            Expr const * call = calls_.top();
+            CallExpr::const_arg_iterator argsBeg;
+            CallExpr::const_arg_iterator argsEnd;
+            if (isa<CallExpr>(call)) {
+                argsBeg = cast<CallExpr>(call)->arg_begin();
+                argsEnd = cast<CallExpr>(call)->arg_end();
+            } else if (isa<CXXConstructExpr>(call)) {
+                argsBeg = cast<CXXConstructExpr>(call)->arg_begin();
+                argsEnd = cast<CXXConstructExpr>(call)->arg_end();
+            } else {
+                assert(false);
+            }
+            for (auto i(argsBeg); i != argsEnd; ++i) {
+                Expr const * e = (*i)->IgnoreParenImpCasts();
+                if (isa<MaterializeTemporaryExpr>(e)) {
+                    e = cast<MaterializeTemporaryExpr>(e)->GetTemporaryExpr()
+                        ->IgnoreParenImpCasts();
+                }
+                if (isa<CXXFunctionalCastExpr>(e)) {
+                    e = cast<CXXFunctionalCastExpr>(e)->getSubExpr()
+                        ->IgnoreParenImpCasts();
+                }
+                if (isa<CXXBindTemporaryExpr>(e)) {
+                    e = cast<CXXBindTemporaryExpr>(e)->getSubExpr()
+                        ->IgnoreParenImpCasts();
+                }
+                if (e == expr) {
+                    if (isa<CallExpr>(call)) {
+                        FunctionDecl const * fdecl
+                            = cast<CallExpr>(call)->getDirectCallee();
+                        if (fdecl == nullptr) {
+                            break;
+                        }
+                        std::string callQname(
+                            fdecl->getQualifiedNameAsString());
+                        if (pass == PassThrough::EmptyConstantString) {
+                            if (callQname == "rtl::OUString::equals"
+                                || callQname == "rtl::operator==")
+                            {
+                                report(
+                                    DiagnosticsEngine::Warning,
+                                    ("rewrite call of " + callQname
+                                     + " with construction of " + qname
+                                     + (" with empty string constant argument"
+                                        " as call of rtl::OUString::isEmpty")),
+                                    getMemberLocation(call))
+                                    << call->getSourceRange();
+                                return true;
+                            }
+                            if (callQname == "rtl::operator!=") {
+                                report(
+                                    DiagnosticsEngine::Warning,
+                                    ("rewrite call of " + callQname
+                                     + " with construction of " + qname
+                                     + (" with empty string constant argument"
+                                        " as call of !rtl::OUString::isEmpty")),
+                                    getMemberLocation(call))
+                                    << call->getSourceRange();
+                                return true;
+                            }
+                            if (callQname == "rtl::operator+"
+                                || callQname == "rtl::OUString::operator+=")
+                            {
+                                report(
+                                    DiagnosticsEngine::Warning,
+                                    ("call of " + callQname
+                                     + " with suspicous construction of "
+                                     + qname
+                                     + " with empty string constant argument"),
+                                    getMemberLocation(call))
+                                    << call->getSourceRange();
+                                return true;
+                            }
+                            if (callQname == "rtl::OUString::operator=") {
+                                report(
+                                    DiagnosticsEngine::Warning,
+                                    ("rewrite call of " + callQname
+                                     + " with construction of " + qname
+                                     + (" with empty string constant argument"
+                                        " as call of rtl::OUString::clear")),
+                                    getMemberLocation(call))
+                                    << call->getSourceRange();
+                                return true;
+                            }
+                        } else {
+                            assert(pass == PassThrough::NonEmptyConstantString);
+                            if (callQname == "rtl::OUString::equals") {
+                                report(
+                                    DiagnosticsEngine::Warning,
+                                    ("rewrite call of " + callQname
+                                     + " with construction of " + qname
+                                     + " with " + describeChangeKind(kind)
+                                     + " as operator =="),
+                                    getMemberLocation(call))
+                                    << call->getSourceRange();
+                                return true;
+                            }
+                            if (callQname == "rtl::operator+"
+                                || callQname == "rtl::OUString::operator="
+                                || callQname == "rtl::operator=="
+                                || callQname == "rtl::operator!=")
+                            {
+                                if (callQname == "rtl::operator+") {
+                                    std::string file(
+                                        compiler.getSourceManager().getFilename(
+                                            compiler.getSourceManager()
+                                            .getSpellingLoc(
+                                                expr->getLocStart())));
+                                    if (file
+                                        == (SRCDIR
+                                            "/sal/qa/rtl/strings/test_ostring_concat.cxx")
+                                        || (file
+                                            == (SRCDIR
+                                            "/sal/qa/rtl/strings/test_oustring_concat.cxx")))
+                                    {
+                                        return true;
+                                    }
+                                }
+                                report(
+                                    DiagnosticsEngine::Warning,
+                                    ("elide construction of " + qname + " with "
+                                     + describeChangeKind(kind) + " in call of "
+                                     + callQname),
+                                    getMemberLocation(expr))
+                                    << expr->getSourceRange();
+                                return true;
+                            }
+                        }
+                        return true;
+                    } else if (isa<CXXConstructExpr>(call)) {
+                    } else {
+                        assert(false);
+                    }
+                }
+            }
+        }
+        return true;
+    }
+    return true;
+}
+
+std::string StringConstant::describeChangeKind(ChangeKind kind) {
+    switch (kind) {
+    case ChangeKind::Char:
+        return "string constant argument";
+    case ChangeKind::CharLen:
+        return "string constant and matching length arguments";
+    case ChangeKind::SingleChar:
+        return "ASCII sal_Unicode argument";
+    }
+}
+
+bool StringConstant::isStringConstant(
+    Expr const * expr, unsigned * size, bool * nonAscii, bool * embeddedNuls,
+    bool * terminatingNul)
+{
+    assert(expr != nullptr);
+    assert(size != nullptr);
+    assert(nonAscii != nullptr);
+    assert(embeddedNuls != nullptr);
+    assert(terminatingNul != nullptr);
+    QualType t = expr->getType();
+    if (!(t->isConstantArrayType() && t.isConstQualified()
+          && isPlainChar(t->getAsArrayTypeUnsafe()->getElementType())))
+    {
+        return false;
+    }
+    StringLiteral const * lit = dyn_cast<StringLiteral>(expr);
+    if (lit != nullptr) {
+        if (!lit->isAscii()) {
+            return false;
+        }
+        unsigned n = lit->getLength();
+        bool non = false;
+        bool emb = false;
+        StringRef str = lit->getString();
+        for (unsigned i = 0; i != n; ++i) {
+            if (str[i] == '\0') {
+                emb = true;
+            } else if (static_cast<unsigned char>(str[i]) >= 0x80) {
+                non = true;
+            }
+        }
+        *size = n;
+        *nonAscii = non;
+        *embeddedNuls = emb;
+        *terminatingNul = true;
+        return true;
+    }
+    APValue v;
+    if (!expr->isCXX11ConstantExpr(compiler.getASTContext(), &v)) {
+        return false;
+    }
+    switch (v.getKind()) {
+    case APValue::LValue:
+        {
+            Expr const * e = v.getLValueBase().dyn_cast<Expr const *>();
+            assert(e != nullptr); //TODO???
+            if (!v.getLValueOffset().isZero()) {
+                return false; //TODO
+            }
+            Expr const * e2 = e->IgnoreParenImpCasts();
+            if (e2 != e) {
+                return isStringConstant(
+                    e2, size, nonAscii, embeddedNuls, terminatingNul);
+            }
+            //TODO: string literals are represented as recursive LValues???
+            llvm::APInt n
+                = compiler.getASTContext().getAsConstantArrayType(t)->getSize();
+            assert(n != 0);
+            --n;
+            assert(n.ule(std::numeric_limits<unsigned>::max()));
+            *size = static_cast<unsigned>(n.getLimitedValue());
+            *nonAscii = false; //TODO
+            *embeddedNuls = false; //TODO
+            *terminatingNul = true;
+            return true;
+        }
+    case APValue::Array:
+        {
+            if (v.hasArrayFiller()) { //TODO: handle final NUL filler?
+                return false;
+            }
+            unsigned n = v.getArraySize();
+            assert(n != 0);
+            bool non = false;
+            bool emb = false;
+            for (unsigned i = 0; i != n - 1; ++i) {
+                APValue e(v.getArrayInitializedElt(i));
+                if (!e.isInt()) { //TODO: assert?
+                    return false;
+                }
+                APSInt iv = e.getInt();
+                if (iv == 0) {
+                    emb = true;
+                } else if (iv.uge(0x80)) {
+                    non = true;
+                }
+            }
+            APValue e(v.getArrayInitializedElt(n - 1));
+            if (!e.isInt()) { //TODO: assert?
+                return false;
+            }
+            bool trm = e.getInt() == 0;
+            *size = trm ? n - 1 : n;
+            *nonAscii = non;
+            *embeddedNuls = emb;
+            *terminatingNul = trm;
+            return true;
+        }
+    default:
+        assert(false); //TODO???
+        return false;
+    }
+}
+
+bool StringConstant::isZero(Expr const * expr) {
+    APSInt res;
+    return expr->isIntegerConstantExpr(res, compiler.getASTContext())
+        && res == 0;
+}
+
+void StringConstant::reportChange(
+    Expr const * expr, ChangeKind kind, std::string const & original,
+    std::string const & replacement, PassThrough pass)
+{
+    if (pass != PassThrough::No && !calls_.empty()) {
+        Expr const * call = calls_.top();
+        CallExpr::const_arg_iterator argsBeg;
+        CallExpr::const_arg_iterator argsEnd;
+        if (isa<CallExpr>(call)) {
+            argsBeg = cast<CallExpr>(call)->arg_begin();
+            argsEnd = cast<CallExpr>(call)->arg_end();
+        } else if (isa<CXXConstructExpr>(call)) {
+            argsBeg = cast<CXXConstructExpr>(call)->arg_begin();
+            argsEnd = cast<CXXConstructExpr>(call)->arg_end();
+        } else {
+            assert(false);
+        }
+        for (auto i(argsBeg); i != argsEnd; ++i) {
+            Expr const * e = (*i)->IgnoreParenImpCasts();
+            if (isa<CXXBindTemporaryExpr>(e)) {
+                e = cast<CXXBindTemporaryExpr>(e)->getSubExpr()
+                    ->IgnoreParenImpCasts();
+            }
+            if (e == expr) {
+                if (isa<CallExpr>(call)) {
+                    FunctionDecl const * fdecl
+                        = cast<CallExpr>(call)->getDirectCallee();
+                    if (fdecl == nullptr) {
+                        break;
+                    }
+                    std::string qname(fdecl->getQualifiedNameAsString());
+                    if (pass == PassThrough::EmptyConstantString) {
+                        if (qname == "rtl::OUString::equals"
+                            || qname == "rtl::operator==")
+                        {
+                            report(
+                                DiagnosticsEngine::Warning,
+                                ("rewrite call of " + qname + " with call of "
+                                 + original
+                                 + (" with empty string constant argument as"
+                                    " call of rtl::OUString::isEmpty")),
+                                getMemberLocation(call))
+                                << call->getSourceRange();
+                            return;
+                        }
+                        if (qname == "rtl::operator!=") {
+                            report(
+                                DiagnosticsEngine::Warning,
+                                ("rewrite call of " + qname + " with call of "
+                                 + original
+                                 + (" with empty string constant argument as"
+                                    " call of !rtl::OUString::isEmpty")),
+                                getMemberLocation(call))
+                                << call->getSourceRange();
+                            return;
+                        }
+                        if (qname == "rtl::operator+"
+                            || qname == "rtl::OUString::operator+=")
+                        {
+                            report(
+                                DiagnosticsEngine::Warning,
+                                ("call of " + qname + " with suspicous call of "
+                                 + original
+                                 + " with empty string constant argument"),
+                                getMemberLocation(call))
+                                << call->getSourceRange();
+                            return;
+                        }
+                        if (qname == "rtl::OUString::operator=") {
+                            report(
+                                DiagnosticsEngine::Warning,
+                                ("rewrite call of " + qname + " with call of "
+                                 + original
+                                 + (" with empty string constant argument as"
+                                    " call of rtl::OUString::call")),
+                                getMemberLocation(call))
+                                << call->getSourceRange();
+                            return;
+                        }
+                    } else {
+                        assert(pass == PassThrough::NonEmptyConstantString);
+                        if (qname == "rtl::OUString::equals"
+                            || qname == "rtl::OUString::operator="
+                            || qname == "rtl::operator=="
+                            || qname == "rtl::operator!=")
+                        {
+                            report(
+                                DiagnosticsEngine::Warning,
+                                ("elide call of " + original + " with "
+                                 + describeChangeKind(kind) + " in call of "
+                                 + qname),
+                                getMemberLocation(expr))
+                                << expr->getSourceRange();
+                            return;
+                        }
+                        if (qname == "rtl::operator+"
+                            || qname == "rtl::OUString::operator+=")
+                        {
+                            report(
+                                DiagnosticsEngine::Warning,
+                                ("rewrite call of " + original + " with "
+                                 + describeChangeKind(kind) + " in call of "
+                                 + qname
+                                 + (" as (implicit) construction of"
+                                    " rtl::OUString")),
+                                getMemberLocation(expr))
+                                << expr->getSourceRange();
+                            return;
+                        }
+                    }
+                    report(
+                        DiagnosticsEngine::Warning,
+                        "TODO call inside " + qname, getMemberLocation(expr))
+                        << expr->getSourceRange();
+                    return;
+                } else if (isa<CXXConstructExpr>(call)) {
+                    std::string qname(
+                        cast<CXXConstructExpr>(call)->getConstructor()
+                        ->getParent()->getQualifiedNameAsString());
+                    if (qname == "rtl::OUString"
+                        || qname == "rtl::OUStringBuffer")
+                    {
+                        //TODO: propagate further out?
+                        if (pass == PassThrough::EmptyConstantString) {
+                            report(
+                                DiagnosticsEngine::Warning,
+                                ("rewrite construction of " + qname
+                                 + " with call of " + original
+                                 + (" with empty string constant argument as"
+                                    " default construction of ")
+                                 + qname),
+                                getMemberLocation(call))
+                                << call->getSourceRange();
+                        } else {
+                            assert(pass == PassThrough::NonEmptyConstantString);
+                            report(
+                                DiagnosticsEngine::Warning,
+                                ("elide call of " + original + " with "
+                                 + describeChangeKind(kind)
+                                 + " in construction of " + qname),
+                                getMemberLocation(expr))
+                                << expr->getSourceRange();
+                        }
+                        return;
+                    }
+                } else {
+                    assert(false);
+                }
+            }
+        }
+    }
+    report(
+        DiagnosticsEngine::Warning,
+        ("rewrite call of " + original + " with " + describeChangeKind(kind)
+         + " as call of " + replacement),
+        getMemberLocation(expr))
+        << expr->getSourceRange();
+}
+
+void StringConstant::checkEmpty(
+    CallExpr const * expr, std::string const & qname, TreatEmpty treatEmpty,
+    unsigned size, std::string * replacement)
+{
+    assert(replacement != nullptr);
+    if (size == 0) {
+        switch (treatEmpty) {
+        case TreatEmpty::DefaultCtor:
+            *replacement = "rtl::OUString default constructor";
+            break;
+        case TreatEmpty::CheckEmpty:
+            *replacement = "rtl::OUString::isEmpty";
+            break;
+        case TreatEmpty::Error:
+            report(
+                DiagnosticsEngine::Warning,
+                ("call of " + qname
+                 + " with suspicous empty string constant argument"),
+                getMemberLocation(expr))
+                << expr->getSourceRange();
+            break;
+        }
+    }
+}
+
+void StringConstant::handleChar(
+    CallExpr const * expr, unsigned arg, std::string const & qname,
+    std::string const & replacement, TreatEmpty treatEmpty, bool literal)
+{
+    unsigned n;
+    bool non;
+    bool emb;
+    bool trm;
+    if (!isStringConstant(
+            expr->getArg(arg)->IgnoreParenImpCasts(), &n, &non, &emb, &trm))
+    {
+        return;
+    }
+    if (non) {
+        report(
+            DiagnosticsEngine::Warning,
+            ("call of " + qname
+             + (" with string constant argument containging non-ASCII"
+                " characters")),
+            getMemberLocation(expr))
+            << expr->getSourceRange();
+        return;
+    }
+    if (emb) {
+        report(
+            DiagnosticsEngine::Warning,
+            ("call of " + qname
+             + " with string constant argument containging embedded NULs"),
+            getMemberLocation(expr))
+            << expr->getSourceRange();
+        return;
+    }
+    if (!trm) {
+        report(
+            DiagnosticsEngine::Warning,
+            ("call of " + qname
+             + " with string constant argument lacking a terminating NUL"),
+            getMemberLocation(expr))
+            << expr->getSourceRange();
+        return;
+    }
+    std::string repl(replacement);
+    checkEmpty(expr, qname, treatEmpty, n, &repl);
+    reportChange(
+        expr, ChangeKind::Char, qname, repl,
+        (literal
+         ? (n == 0
+            ? PassThrough::EmptyConstantString
+            : PassThrough::NonEmptyConstantString)
+         : PassThrough::No));
+}
+
+void StringConstant::handleCharLen(
+    CallExpr const * expr, unsigned arg1, unsigned arg2,
+    std::string const & qname, std::string const & replacement,
+    TreatEmpty treatEmpty)
+{
+    // Especially for f(RTL_CONSTASCII_STRINGPARAM("foo")), where
+    // RTL_CONSTASCII_STRINGPARAM expands to complicated expressions involving
+    // (&(X)[0] sub-expressions (and it might or might not be better to handle
+    // that at the level of non-expanded macros instead, but I have not found
+    // out how to do that yet anyway):
+    unsigned n;
+    bool non;
+    bool emb;
+    bool trm;
+    if (!(isStringConstant(
+              expr->getArg(arg1)->IgnoreParenImpCasts(), &n, &non, &emb, &trm)
+          && trm))
+    {
+        return;
+    }
+    APSInt res;
+    if (expr->getArg(arg2)->isIntegerConstantExpr(
+            res, compiler.getASTContext()))
+    {
+        if (res != n) {
+            return;
+        }
+    } else {
+        UnaryOperator const * op = dyn_cast<UnaryOperator>(
+            expr->getArg(arg1)->IgnoreParenImpCasts());
+        if (op == nullptr || op->getOpcode() != UO_AddrOf) {
+            return;
+        }
+        ArraySubscriptExpr const * subs = dyn_cast<ArraySubscriptExpr>(
+            op->getSubExpr()->IgnoreParenImpCasts());
+        if (subs == nullptr) {
+            return;
+        }
+        unsigned n2;
+        bool non2;
+        bool emb2;
+        bool trm2;
+        if (!(isStringConstant(
+                  subs->getBase()->IgnoreParenImpCasts(), &n2, &non2, &emb2,
+                  &trm2)
+              && n2 == n && non2 == non && emb2 == emb && trm2 == trm
+                  //TODO: same strings
+              && subs->getIdx()->isIntegerConstantExpr(
+                  res, compiler.getASTContext())
+              && res == 0))
+        {
+            return;
+        }
+    }
+    if (non) {
+        report(
+            DiagnosticsEngine::Warning,
+            ("call of " + qname
+             + (" with string constant argument containging non-ASCII"
+                " characters")),
+            getMemberLocation(expr))
+            << expr->getSourceRange();
+    }
+    if (emb) {
+        return;
+    }
+    std::string repl(replacement);
+    checkEmpty(expr, qname, treatEmpty, n, &repl);
+    reportChange(expr, ChangeKind::CharLen, qname, repl, PassThrough::No);
+}
+
+loplugin::Plugin::Registration< StringConstant > X("stringconstant");
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 47debe4f1b1024095651355797150507a1d66a6f
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Fri Dec 19 09:48:40 2014 +0100

    ucb: Use appropriate OUString functions on string constants
    
    Change-Id: I29c99157e9698e4af1d42a5c708cf4ec4753cf34

diff --git a/ucb/source/ucp/gio/gio_content.cxx b/ucb/source/ucp/gio/gio_content.cxx
index 2eb2219..d821e6b 100644
--- a/ucb/source/ucp/gio/gio_content.cxx
+++ b/ucb/source/ucp/gio/gio_content.cxx
@@ -1146,13 +1146,13 @@ uno::Sequence< ucb::ContentInfo > Content::queryCreatableContentsInfo(
             beans::PropertyAttribute::MAYBEVOID | beans::PropertyAttribute::BOUND );
 
         // file
-        seq[0].Type       = OUString( GIO_FILE_TYPE );
+        seq[0].Type       = GIO_FILE_TYPE;
         seq[0].Attributes = ( ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM |
                               ucb::ContentInfoAttribute::KIND_DOCUMENT );
         seq[0].Properties = props;
 
         // folder
-        seq[1].Type       = OUString( GIO_FOLDER_TYPE );
+        seq[1].Type       = GIO_FOLDER_TYPE;
         seq[1].Attributes = ucb::ContentInfoAttribute::KIND_FOLDER;
         seq[1].Properties = props;
 
diff --git a/ucb/source/ucp/gio/gio_provider.cxx b/ucb/source/ucp/gio/gio_provider.cxx
index ce8654f..acc8f1e 100644
--- a/ucb/source/ucp/gio/gio_provider.cxx
+++ b/ucb/source/ucp/gio/gio_provider.cxx
@@ -105,7 +105,7 @@ XTYPEPROVIDER_IMPL_3( ContentProvider,
 
 XSERVICEINFO_IMPL_1_CTX( ContentProvider,
                      OUString( "com.sun.star.comp.GIOContentProvider" ),
-                     OUString( "com.sun.star.ucb.GIOContentProvider" ) );
+                     "com.sun.star.ucb.GIOContentProvider" );
 
 ONE_INSTANCE_SERVICE_FACTORY_IMPL( ContentProvider );
 
commit 36398245327a03d8228cf0a2b093ae5e689d2a26
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Fri Dec 19 09:48:23 2014 +0100

    extensions: Use appropriate OUString functions on string constants
    
    Change-Id: Ief21a59fc58cdd6fb3398bbe5080ed5a53cb374e

diff --git a/extensions/source/plugin/base/xplugin.cxx b/extensions/source/plugin/base/xplugin.cxx
index a151bc9..1675d7e 100644
--- a/extensions/source/plugin/base/xplugin.cxx
+++ b/extensions/source/plugin/base/xplugin.cxx
@@ -312,7 +312,7 @@ void XPlugin_Impl::handleSpecialArgs()
 {
     // special handling for real audio which needs a lot of parameters
     // or won't function at all
-    if( m_aDescription.Mimetype.equalsAscii( "audio/x-pn-realaudio-plugin" ) && m_nArgs < 1 )
+    if( m_aDescription.Mimetype == "audio/x-pn-realaudio-plugin" && m_nArgs < 1 )
     {
         OUString aURL;
         if( m_xModel.is() )
@@ -356,7 +356,7 @@ void XPlugin_Impl::handleSpecialArgs()
         }
     }
     // #69333# special for pdf
-    else if( m_aDescription.Mimetype.equalsAscii( "application/pdf" ) )
+    else if( m_aDescription.Mimetype == "application/pdf" )
         m_aPluginMode = PluginMode::FULL;
 
     // see if we have a TYPE tag
@@ -847,7 +847,7 @@ void XPlugin_Impl::propertyChange(const com::sun::star::beans::PropertyChangeEve
 {
     Guard< Mutex > aGuard( m_aMutex );
 
-    if( rEvent.PropertyName.equalsAscii( "URL" ) )
+    if( rEvent.PropertyName == "URL" )
     {
         OUString aStr;
         rEvent.NewValue >>= aStr;
commit 4524c5dd47b90701e7f068c2978454828f646414
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Fri Dec 19 09:48:07 2014 +0100

    desktop: Use appropriate OUString functions on string constants
    
    Change-Id: I8a7d6609466f450e0bb998457d8bf16525152091

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 40cc467..b3593d0 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -293,7 +293,7 @@ static LibreOfficeKitDocument* lo_documentLoad(LibreOfficeKit* pThis, const char
 
     OUString aURL = getAbsoluteURL(pURL);
 
-    pLib->maLastExceptionMsg = "";
+    pLib->maLastExceptionMsg.clear();
 
     if (!xContext.is())
     {


More information about the Libreoffice-commits mailing list