Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/compilerplugins/clang/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 86 kB image not shown  

Quelle  stringconstant.cxx   Sprache: C

 
/* -*- 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 <algorithm>
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <iomanip>
#include <limits>
#include <sstream>
#include <stack>
#include <string>
#include <vector>
#include <iostream>

#include "check.hxx"
#include "compat.hxx"
#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-NULL ASCII character---except
// that the last array element may be NULL, or, in some situations, of type char
// with an ASCII value (including NULL).  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 {

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();
}

bool isLhsOfAssignment(FunctionDecl const * decl, unsigned parameter) {
    if (parameter != 0) {
        return false;
    }
    auto oo = decl->getOverloadedOperator();
    return oo == OO_Equal
        || (oo >= OO_PlusEqual && oo <= OO_GreaterGreaterEqual);
}

bool hasOverloads(FunctionDecl const * decl, unsigned arguments) {
    int n = 0;
    auto ctx = decl->getDeclContext();
    if (ctx->getDeclKind() == Decl::LinkageSpec) {
        ctx = ctx->getParent();
    }
    auto res = ctx->lookup(decl->getDeclName());
    for (auto d = res.begin(); d != res.end(); ++d) {
        FunctionDecl const * f = dyn_cast<FunctionDecl>(*d);
        if (f != nullptr && f->getMinRequiredArguments() <= arguments
            && f->getNumParams() >= arguments)
        {
            auto consDecl = dyn_cast<CXXConstructorDecl>(f);
            if (consDecl && consDecl->isCopyOrMoveConstructor()) {
                continue;
            }
            ++n;
            if (n == 2) {
                return true;
            }
        }
    }
    return false;
}

CXXConstructExpr const * lookForCXXConstructExpr(Expr const * expr) {
    if (auto e = dyn_cast<MaterializeTemporaryExpr>(expr)) {
        expr = e->getSubExpr();
    }
    if (auto e = dyn_cast<CXXFunctionalCastExpr>(expr)) {
        expr = e->getSubExpr();
    }
    if (auto e = dyn_cast<CXXBindTemporaryExpr>(expr)) {
        expr = e->getSubExpr();
    }
    if (auto const e = dyn_cast<CXXMemberCallExpr>(expr)) {
        // Look through OString::operator std::string_view:
        if (isa_and_nonnull<CXXConversionDecl>(e->getCalleeDecl())) {
            return lookForCXXConstructExpr(e->getImplicitObjectArgument()->IgnoreParenImpCasts());
        }
    }
    return dyn_cast<CXXConstructExpr>(expr);
}

char const * adviseNonArray(bool nonArray) {
    return nonArray
        ? ", and turn the non-array string constant into an array" : "";
}

class StringConstant:
    public loplugin::FilteringRewritePlugin<StringConstant>
{
public:
    explicit StringConstant(loplugin::InstantiationData const & data):
        FilteringRewritePlugin(data) {}

    void run() override;

    bool TraverseFunctionDecl(FunctionDecl * decl) {
        returnTypes_.push(decl->getDeclaredReturnType());
        auto const ret = RecursiveASTVisitor::TraverseFunctionDecl(decl);
        assert(!returnTypes_.empty());
        assert(returnTypes_.top() == decl->getDeclaredReturnType());
        returnTypes_.pop();
        return ret;
    }

    bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl * decl) {
        returnTypes_.push(decl->getDeclaredReturnType());
        auto const ret = RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(
            decl);
        assert(!returnTypes_.empty());
        assert(returnTypes_.top() == decl->getDeclaredReturnType());
        returnTypes_.pop();
        return ret;
    }

    bool TraverseCXXMethodDecl(CXXMethodDecl * decl) {
        returnTypes_.push(decl->getDeclaredReturnType());
        auto const ret = RecursiveASTVisitor::TraverseCXXMethodDecl(decl);
        assert(!returnTypes_.empty());
        assert(returnTypes_.top() == decl->getDeclaredReturnType());
        returnTypes_.pop();
        return ret;
    }

    bool TraverseCXXConstructorDecl(CXXConstructorDecl * decl) {
        returnTypes_.push(decl->getDeclaredReturnType());
        auto const ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(decl);
        assert(!returnTypes_.empty());
        assert(returnTypes_.top() == decl->getDeclaredReturnType());
        returnTypes_.pop();
        return ret;
    }

    bool TraverseCXXDestructorDecl(CXXDestructorDecl * decl) {
        returnTypes_.push(decl->getDeclaredReturnType());
        auto const ret = RecursiveASTVisitor::TraverseCXXDestructorDecl(decl);
        assert(!returnTypes_.empty());
        assert(returnTypes_.top() == decl->getDeclaredReturnType());
        returnTypes_.pop();
        return ret;
    }

    bool TraverseCXXConversionDecl(CXXConversionDecl * decl) {
        returnTypes_.push(decl->getDeclaredReturnType());
        auto const ret = RecursiveASTVisitor::TraverseCXXConversionDecl(decl);
        assert(!returnTypes_.empty());
        assert(returnTypes_.top() == decl->getDeclaredReturnType());
        returnTypes_.pop();
        return ret;
    }

    bool TraverseObjCMethodDecl(ObjCMethodDecl * decl) {
        returnTypes_.push(decl->getReturnType());
        auto const ret = RecursiveASTVisitor::TraverseObjCMethodDecl(decl);
        assert(!returnTypes_.empty());
        assert(returnTypes_.top() == decl->getReturnType());
        returnTypes_.pop();
        return ret;
    }

    bool TraverseCallExpr(CallExpr * expr);

    bool TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr);

    bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr * expr);

    bool TraverseCXXConstructExpr(CXXConstructExpr * expr);

    bool VisitCallExpr(CallExpr const * expr);

    bool VisitCXXMemberCallExpr(CXXMemberCallExpr const * expr);

    bool VisitCXXConstructExpr(CXXConstructExpr const * expr);

    bool VisitReturnStmt(ReturnStmt const * stmt);

private:
    enum class ContentKind { Ascii, Utf8, Arbitrary };

    enum class TreatEmpty { DefaultCtor, CheckEmpty, Error };

    enum class ChangeKind { Char, CharLen, SingleChar, OUStringChar };

    enum class PassThrough { No, EmptyConstantString, NonEmptyConstantString };

    std::string describeChangeKind(ChangeKind kind);

    bool isStringConstant(
        Expr const * expr, unsigned * size, bool * nonArray,
        ContentKind * content, bool * embeddedNuls, bool * terminatingNul,
        std::vector<char32_t> * utf8Content = nullptr);

    bool isZero(Expr const * expr);

    void reportChange(
        Expr const * expr, ChangeKind kind, std::string const & original,
        std::string const & replacement, PassThrough pass, bool nonArray,
        char const * rewriteFrom, char const * rewriteTo);

    void checkEmpty(
        CallExpr const * expr, FunctionDecl const * callee,
        TreatEmpty treatEmpty, unsigned size, std::string * replacement);

    void handleChar(
        CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
        std::string const & replacement, TreatEmpty treatEmpty, bool literal,
    char const * rewriteFrom = nullptr, char const * rewriteTo = nullptr);

    void handleCharLen(
        CallExpr const * expr, unsigned arg1, unsigned arg2,
        FunctionDecl const * callee, std::string const & replacement,
        TreatEmpty treatEmpty);

    void handleOUStringCtor(
        CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
        bool explicitFunctionalCastNotation);

    void handleOStringCtor(
        CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
        bool explicitFunctionalCastNotation);

    void handleOUStringCtor(
        Expr const * expr, Expr const * argExpr, FunctionDecl const * callee,
        bool explicitFunctionalCastNotation);

    void handleOStringCtor(
        Expr const * expr, Expr const * argExpr, FunctionDecl const * callee,
        bool explicitFunctionalCastNotation);

    enum class StringKind { Unicode, Char };
    void handleStringCtor(
        Expr const * expr, Expr const * argExpr, FunctionDecl const * callee,
        bool explicitFunctionalCastNotation, StringKind stringKind);

    void handleFunArgOstring(
        CallExpr const * expr, unsigned arg, FunctionDecl const * callee);

    std::stack<QualType> returnTypes_;
    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 bRes = true;
    for (auto * e: expr->children()) {
        if (!TraverseStmt(e)) {
            bRes = false;
            break;
        }
    }
    calls_.pop();
    return bRes;
}

bool StringConstant::TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr) {
    if (!WalkUpFromCXXMemberCallExpr(expr)) {
        return false;
    }
    calls_.push(expr);
    bool bRes = true;
    for (auto * e: expr->children()) {
        if (!TraverseStmt(e)) {
            bRes = false;
            break;
        }
    }
    calls_.pop();
    return bRes;
}

bool StringConstant::TraverseCXXOperatorCallExpr(CXXOperatorCallExpr * expr)
{
    if (!WalkUpFromCXXOperatorCallExpr(expr)) {
        return false;
    }
    calls_.push(expr);
    bool bRes = true;
    for (auto * e: expr->children()) {
        if (!TraverseStmt(e)) {
            bRes = false;
            break;
        }
    }
    calls_.pop();
    return bRes;
}

bool StringConstant::TraverseCXXConstructExpr(CXXConstructExpr * expr) {
    if (!WalkUpFromCXXConstructExpr(expr)) {
        return false;
    }
    calls_.push(expr);
    bool bRes = true;
    for (auto * e: expr->children()) {
        if (!TraverseStmt(e)) {
            bRes = false;
            break;
        }
    }
    calls_.pop();
    return bRes;
}

bool StringConstant::VisitCallExpr(CallExpr const * expr) {
    if (ignoreLocation(expr)) {
        return true;
    }
    FunctionDecl const * fdecl = expr->getDirectCallee();
    if (fdecl == nullptr) {
        return true;
    }
    for (unsigned i = 0; i != fdecl->getNumParams(); ++i) {
        auto t = fdecl->getParamDecl(i)->getType();
        if (loplugin::TypeCheck(t).NotSubstTemplateTypeParmType()
            .LvalueReference().Const().NotSubstTemplateTypeParmType()
            .Class("OUString").Namespace("rtl").GlobalNamespace())
        {
            if (!(isLhsOfAssignment(fdecl, i)
                  || hasOverloads(fdecl, expr->getNumArgs())))
            {
                handleOUStringCtor(expr, i, fdecl, true);
            }
        }
        if (loplugin::TypeCheck(t).NotSubstTemplateTypeParmType()
            .LvalueReference().Const().NotSubstTemplateTypeParmType()
            .Class("OString").Namespace("rtl").GlobalNamespace())
        {
            if (!(isLhsOfAssignment(fdecl, i)
                  || hasOverloads(fdecl, expr->getNumArgs())))
            {
                handleOStringCtor(expr, i, fdecl, true);
            }
        }
    }
    loplugin::DeclCheck dc(fdecl);
    //TODO: u.compareToAscii("foo") -> u.???("foo")
    //TODO: u.compareToIgnoreAsciiCaseAscii("foo") -> u.???("foo")
    if ((dc.Function("createFromAscii").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 1)
    {
        // OUString::createFromAscii("foo") -> OUString("foo")
        handleChar(
            expr, 0, fdecl, "rtl::OUString constructor",
            TreatEmpty::DefaultCtor, true);
        return true;
    }
    if ((dc.Function("endsWithAsciiL").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        // u.endsWithAsciiL("foo", 3) -> u.endsWith("foo"):
        handleCharLen(
            expr, 0, 1, fdecl, "rtl::OUString::endsWith", TreatEmpty::Error);
        return true;
    }
    if ((dc.Function("endsWithIgnoreAsciiCaseAsciiL").Class("OUString")
         .Namespace("rtl").GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        // u.endsWithIgnoreAsciiCaseAsciiL("foo", 3) ->
        // u.endsWithIgnoreAsciiCase("foo"):
        handleCharLen(
            expr, 0, 1, fdecl, "rtl::OUString::endsWithIgnoreAsciiCase",
            TreatEmpty::Error);
        return true;
    }
    if ((dc.Function("equalsAscii").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 1)
    {
        // u.equalsAscii("foo") -> u == "foo":
        handleChar(
            expr, 0, fdecl, "operator ==", TreatEmpty::CheckEmpty, false);
        return true;
    }
    if ((dc.Function("equalsAsciiL").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        // u.equalsAsciiL("foo", 3) -> u == "foo":
        handleCharLen(expr, 0, 1, fdecl, "operator ==", TreatEmpty::CheckEmpty);
        return true;
    }
    if ((dc.Function("equalsIgnoreAsciiCaseAscii").Class("OUString")
         .Namespace("rtl").GlobalNamespace())
        && fdecl->getNumParams() == 1)
    {
        // u.equalsIgnoreAsciiCaseAscii("foo") ->
        // u.equalsIngoreAsciiCase("foo"):

        auto file = getFilenameOfLocation(
            compiler.getSourceManager().getSpellingLoc(expr->getBeginLoc()));
        if (loplugin::isSamePathname(
                file, SRCDIR "/sal/qa/rtl/strings/test_oustring_compare.cxx"))
        {
            return true;
        }
        handleChar(
            expr, 0, fdecl, "rtl::OUString::equalsIgnoreAsciiCase",
            TreatEmpty::CheckEmpty, false);
        return true;
    }
    if ((dc.Function("equalsIgnoreAsciiCaseAsciiL").Class("OUString")
         .Namespace("rtl").GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        // u.equalsIgnoreAsciiCaseAsciiL("foo", 3) ->
        // u.equalsIngoreAsciiCase("foo"):
        auto file = getFilenameOfLocation(
            compiler.getSourceManager().getSpellingLoc(expr->getBeginLoc()));
        if (loplugin::isSamePathname(
                file, SRCDIR "/sal/qa/rtl/strings/test_oustring_compare.cxx"))
        {
            return true;
        }
        handleCharLen(
            expr, 0, 1, fdecl, "rtl::OUString::equalsIgnoreAsciiCase",
            TreatEmpty::CheckEmpty);
        return true;
    }
    if ((dc.Function("indexOfAsciiL").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 3)
    {
        assert(expr->getNumArgs() == 3);
        // u.indexOfAsciiL("foo", 3, i) -> u.indexOf("foo", i):
        handleCharLen(
            expr, 0, 1, fdecl, "rtl::OUString::indexOf", TreatEmpty::Error);
        return true;
    }
    if ((dc.Function("lastIndexOfAsciiL").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        // u.lastIndexOfAsciiL("foo", 3) -> u.lastIndexOf("foo"):
        handleCharLen(
            expr, 0, 1, fdecl, "rtl::OUString::lastIndexOf", TreatEmpty::Error);
        return true;
    }
    if ((dc.Function("matchAsciiL").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 3)
    {
        assert(expr->getNumArgs() == 3);
        // u.matchAsciiL("foo", 3, i) -> u.match("foo", i):
        handleCharLen(
            expr, 0, 1, fdecl,
            (isZero(expr->getArg(2))
             ? std::string("rtl::OUString::startsWith")
             : std::string("rtl::OUString::match")),
            TreatEmpty::Error);
        return true;
    }
    if ((dc.Function("matchIgnoreAsciiCaseAsciiL").Class("OUString")
         .Namespace("rtl").GlobalNamespace())
        && fdecl->getNumParams() == 3)
    {
        assert(expr->getNumArgs() == 3);
        // u.matchIgnoreAsciiCaseAsciiL("foo", 3, i) ->
        // u.matchIgnoreAsciiCase("foo", i):
        handleCharLen(
            expr, 0, 1, fdecl,
            (isZero(expr->getArg(2))
             ? std::string("rtl::OUString::startsWithIgnoreAsciiCase")
             : std::string("rtl::OUString::matchIgnoreAsciiCase")),
            TreatEmpty::Error);
        return true;
    }
    if ((dc.Function("reverseCompareToAsciiL").Class("OUString")
         .Namespace("rtl").GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        // u.reverseCompareToAsciiL("foo", 3) -> u.reverseCompareTo("foo"):
        handleCharLen(
            expr, 0, 1, fdecl, "rtl::OUString::reverseCompareTo",
            TreatEmpty::Error);
        return true;
    }
    if ((dc.Function("reverseCompareTo").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 1)
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        return true;
    }
    if ((dc.Function("equalsIgnoreAsciiCase").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 1)
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        return true;
    }
    if ((dc.Function("match").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        return true;
    }
    if ((dc.Function("matchIgnoreAsciiCase").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        return true;
    }
    if ((dc.Function("startsWith").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        return true;
    }
    if ((dc.Function("startsWithIgnoreAsciiCase").Class("OUString")
         .Namespace("rtl").GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        return true;
    }
    if ((dc.Function("endsWith").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        return true;
    }
    if ((dc.Function("endsWithIgnoreAsciiCase").Class("OUString")
         .Namespace("rtl").GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        return true;
    }
    if ((dc.Function("indexOf").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        return true;
    }
    if ((dc.Function("lastIndexOf").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 1)
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        return true;
    }
    if ((dc.Function("replaceFirst").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 3)
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        handleOUStringCtor(expr, 1, fdecl, false);
        return true;
    }
    if ((dc.Function("replaceAll").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && (fdecl->getNumParams() == 2 || fdecl->getNumParams() == 3))
    {
        handleOUStringCtor(expr, 0, fdecl, false);
        handleOUStringCtor(expr, 1, fdecl, false);
        return true;
    }
    if ((dc.Operator(OO_PlusEqual).Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 1)
    {
        handleOUStringCtor(
            expr, dyn_cast<CXXOperatorCallExpr>(expr) == nullptr ? 0 : 1,
            fdecl, false);
        return true;
    }
    if ((dc.Function("equals").Class("OUString").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 1)
    {
        unsigned n;
        bool nonArray;
        ContentKind cont;
        bool emb;
        bool trm;
        if (!isStringConstant(
                expr->getArg(0)->IgnoreParenImpCasts(), &n, &nonArray, &cont,
                &emb, &trm))
        {
            return true;
        }
        if (cont != ContentKind::Ascii) {
            report(
                DiagnosticsEngine::Warning,
                ("call of '%0' with string constant argument containing"
                 " non-ASCII characters"),
                expr->getExprLoc())
                << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
        }
        if (emb) {
            report(
                DiagnosticsEngine::Warning,
                ("call of '%0' with string constant argument containing"
                 " embedded NULLs"),
                expr->getExprLoc())
                << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
        }
        if (n == 0) {
            report(
                DiagnosticsEngine::Warning,
                ("rewrite call of '%0' with empty string constant argument as"
                 " call of 'rtl::OUString::isEmpty'"),
                expr->getExprLoc())
                << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
            return true;
        }
    }
    if (dc.Operator(OO_EqualEqual).Namespace("rtl").GlobalNamespace()
        && fdecl->getNumParams() == 2)
    {
        for (unsigned i = 0; i != 2; ++i) {
            unsigned n;
            bool nonArray;
            ContentKind cont;
            bool emb;
            bool trm;
            if (!isStringConstant(
                    expr->getArg(i)->IgnoreParenImpCasts(), &n, &nonArray,
                    &cont, &emb, &trm))
            {
                continue;
            }
            if (cont != ContentKind::Ascii) {
                report(
                    DiagnosticsEngine::Warning,
                    ("call of '%0' with string constant argument containing"
                     " non-ASCII characters"),
                    expr->getExprLoc())
                    << fdecl->getQualifiedNameAsString()
                    << expr->getSourceRange();
            }
            if (emb) {
                report(
                    DiagnosticsEngine::Warning,
                    ("call of '%0' with string constant argument containing"
                     " embedded NULLs"),
                    expr->getExprLoc())
                    << fdecl->getQualifiedNameAsString()
                    << expr->getSourceRange();
            }
            if (n == 0) {
                report(
                    DiagnosticsEngine::Warning,
                    ("rewrite call of '%0' with empty string constant argument"
                     " as call of 'rtl::OUString::isEmpty'"),
                    expr->getExprLoc())
                    << fdecl->getQualifiedNameAsString()
                    << expr->getSourceRange();
            }
        }
        return true;
    }
    if (dc.Operator(OO_ExclaimEqual).Namespace("rtl").GlobalNamespace()
        && fdecl->getNumParams() == 2)
    {
        for (unsigned i = 0; i != 2; ++i) {
            unsigned n;
            bool nonArray;
            ContentKind cont;
            bool emb;
            bool trm;
            if (!isStringConstant(
                    expr->getArg(i)->IgnoreParenImpCasts(), &n, &nonArray,
                    &cont, &emb, &trm))
            {
                continue;
            }
            if (cont != ContentKind::Ascii) {
                report(
                    DiagnosticsEngine::Warning,
                    ("call of '%0' with string constant argument containing"
                     " non-ASCII characters"),
                    expr->getExprLoc())
                    << fdecl->getQualifiedNameAsString()
                    << expr->getSourceRange();
            }
            if (emb) {
                report(
                    DiagnosticsEngine::Warning,
                    ("call of '%0' with string constant argument containing"
                     " embedded NULLs"),
                    expr->getExprLoc())
                    << fdecl->getQualifiedNameAsString()
                    << expr->getSourceRange();
            }
            if (n == 0) {
                report(
                    DiagnosticsEngine::Warning,
                    ("rewrite call of '%0' with empty string constant argument"
                     " as call of '!rtl::OUString::isEmpty'"),
                    expr->getExprLoc())
                    << fdecl->getQualifiedNameAsString()
                    << expr->getSourceRange();
            }
        }
        return true;
    }
    if (dc.Operator(OO_Equal).Namespace("rtl").GlobalNamespace()
        && fdecl->getNumParams() == 1)
    {
        unsigned n;
        bool nonArray;
        ContentKind cont;
        bool emb;
        bool trm;
        if (!isStringConstant(
                expr->getArg(1)->IgnoreParenImpCasts(), &n, &nonArray, &cont,
                &emb, &trm))
        {
            return true;
        }
        if (cont != ContentKind::Ascii) {
            report(
                DiagnosticsEngine::Warning,
                ("call of '%0' with string constant argument containing"
                 " non-ASCII characters"),
                expr->getExprLoc())
                << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
        }
        if (emb) {
            report(
                DiagnosticsEngine::Warning,
                ("call of '%0' with string constant argument containing"
                 " embedded NULLs"),
                expr->getExprLoc())
                << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
        }
        if (n == 0) {
            report(
                DiagnosticsEngine::Warning,
                ("rewrite call of '%0' with empty string constant argument as"
                 " call of 'rtl::OUString::clear'"),
                expr->getExprLoc())
                << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
            return true;
        }
        return true;
    }
    if (dc.Function("append").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
        && fdecl->getNumParams() == 1)
    {
        handleChar(expr, 0, fdecl, "", TreatEmpty::Error, false);
        return true;
    }
    if ((dc.Function("appendAscii").Class("OUStringBuffer").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 1)
    {
        // u.appendAscii("foo") -> u.append("foo")
        handleChar(
            expr, 0, fdecl, "rtl::OUStringBuffer::append", TreatEmpty::Error,
            true"appendAscii""append");
        return true;
    }
    if ((dc.Function("appendAscii").Class("OUStringBuffer").Namespace("rtl")
         .GlobalNamespace())
        && fdecl->getNumParams() == 2)
    {
        // u.appendAscii("foo", 3) -> u.append("foo"):
        handleCharLen(
            expr, 0, 1, fdecl, "rtl::OUStringBuffer::append",
            TreatEmpty::Error);
        return true;
    }
    if (dc.Function("append").Class("OStringBuffer").Namespace("rtl")
        .GlobalNamespace())
    {
        switch (fdecl->getNumParams()) {
        case 1:
            handleFunArgOstring(expr, 0, fdecl);
            break;
        case 2:
            {
                // b.append("foo", 3) -> b.append("foo"):
                auto file = getFilenameOfLocation(
                    compiler.getSourceManager().getSpellingLoc(
                        expr->getBeginLoc()));
                if (loplugin::isSamePathname(
                        file,
                        SRCDIR "/sal/qa/OStringBuffer/rtl_OStringBuffer.cxx"))
                {
                    return true;
                }
                handleCharLen(
                    expr, 0, 1, fdecl, "rtl::OStringBuffer::append",
                    TreatEmpty::Error);
            }
            break;
        default:
            break;
        }
        return true;
    }
    if (dc.Function("insert").Class("OStringBuffer").Namespace("rtl")
        .GlobalNamespace())
    {
        switch (fdecl->getNumParams()) {
        case 2:
            handleFunArgOstring(expr, 1, fdecl);
            break;
        case 3:
            {
                // b.insert(i, "foo", 3) -> b.insert(i, "foo"):
                handleCharLen(
                    expr, 1, 2, fdecl, "rtl::OStringBuffer::insert",
                    TreatEmpty::Error);
                break;
            }
        default:
            break;
        }
        return true;
    }
    return true;
}

bool StringConstant::VisitCXXMemberCallExpr(CXXMemberCallExpr const * expr) {
    if (ignoreLocation(expr)) {
        return true;
    }
    FunctionDecl const * fdecl = expr->getDirectCallee();
    if (fdecl == nullptr) {
        return true;
    }
    auto const c = loplugin::DeclCheck(fdecl).Function("getStr");
    if ((c.Class("OString").Namespace("rtl").GlobalNamespace()
         || c.Class("OUString").Namespace("rtl").GlobalNamespace())
        && fdecl->getNumParams() == 0)
    {
        auto const e1 = expr->getImplicitObjectArgument()->IgnoreImplicit()->IgnoreParens();
        if (auto const e2 = dyn_cast<CXXTemporaryObjectExpr>(e1)) {
            if (e2->getNumArgs() != 0) {
                return true;
            }
            report(
                DiagnosticsEngine::Warning,
                "in call of '%0', replace default-constructed %1 directly with an empty %select{ordinary|UTF-16}2 string literal",
                expr->getExprLoc())
                << fdecl->getQualifiedNameAsString() << e2->getType() << bool(loplugin::TypeCheck(e2->getType()).Class("OUString")) << expr->getSourceRange();
            return true;
        }
        if (auto const e2 = dyn_cast<CXXFunctionalCastExpr>(e1)) {
            auto const e3 = dyn_cast<clang::StringLiteral>(e2->getSubExprAsWritten()->IgnoreParens());
            if (e3 == nullptr) {
                return true;
            }
            report(
                DiagnosticsEngine::Warning,
                "in call of '%0', replace %1 constructed from a string literal directly with %select{the|a UTF-16}2 string literal",
                expr->getExprLoc())
                << fdecl->getQualifiedNameAsString() << e2->getType() << (loplugin::TypeCheck(e2->getType()).Class("OUString") && !e3->isUTF16()) << expr->getSourceRange();
            return true;
        }
    }
    return true;
}

bool StringConstant::VisitCXXConstructExpr(CXXConstructExpr const * expr) {
    if (ignoreLocation(expr)) {
        return true;
    }
    auto classdecl = expr->getConstructor()->getParent();
    if (loplugin::DeclCheck(classdecl)
        .Class("OUString").Namespace("rtl").GlobalNamespace())
    {
        ChangeKind kind;
        PassThrough pass;
        bool simplify;
        switch (expr->getConstructor()->getNumParams()) {
        case 1:
            if (!loplugin::TypeCheck(
                    expr->getConstructor()->getParamDecl(0)->getType())
                .Typedef("sal_Unicode").GlobalNamespace())
            {
                return true;
            }
            kind = ChangeKind::SingleChar;
            pass = PassThrough::NonEmptyConstantString;
            simplify = false;
            break;
        case 2:
            {
                auto arg = expr->getArg(0);
                if (loplugin::TypeCheck(arg->getType())
                    .Class("OUStringChar_").Namespace("rtl")
                    .GlobalNamespace())
                {
                    kind = ChangeKind::OUStringChar;
                    pass = PassThrough::NonEmptyConstantString;
                    simplify = false;
                } else {
                    unsigned n;
                    bool nonArray;
                    ContentKind cont;
                    bool emb;
                    bool trm;
                    if (!isStringConstant(
                            arg->IgnoreParenImpCasts(), &n, &nonArray, &cont,
                            &emb, &trm))
                    {
                        return true;
                    }
                    if (cont != ContentKind::Ascii) {
                        report(
                            DiagnosticsEngine::Warning,
                            ("construction of %0 with string constant argument"
                             " containing non-ASCII characters"),
                            expr->getExprLoc())
                            << classdecl << expr->getSourceRange();
                    }
                    if (emb) {
                        report(
                            DiagnosticsEngine::Warning,
                            ("construction of %0 with string constant argument"
                             " containing embedded NULLs"),
                            expr->getExprLoc())
                            << classdecl << expr->getSourceRange();
                    }
                    kind = ChangeKind::Char;
                    pass = n == 0
                        ? PassThrough::EmptyConstantString
                        : PassThrough::NonEmptyConstantString;
                    simplify = false;
                }
                break;
            }
        case 4:
            {
                unsigned n;
                bool nonArray;
                ContentKind cont;
                bool emb;
                bool trm;
                std::vector<char32_t> utf8Cont;
                if (!isStringConstant(
                        expr->getArg(0)->IgnoreParenImpCasts(), &n, &nonArray,
                        &cont, &emb, &trm, &utf8Cont))
                {
                    return true;
                }
                APSInt res;
                if (!compat::EvaluateAsInt(expr->getArg(1),
                        res, compiler.getASTContext()))
                {
                    return true;
                }
                if (res != n) {
                    report(
                        DiagnosticsEngine::Warning,
                        ("suspicious 'rtl::OUString' constructor with literal"
                         " of length %0 and non-matching length argument %1"),
                        expr->getExprLoc())
                        << n << compat::toString(res, 10) << expr->getSourceRange();
                    return true;
                }
                APSInt enc;
                if (!compat::EvaluateAsInt(expr->getArg(2),
                        enc, compiler.getASTContext()))
                {
                    return true;
                }
                auto const encIsAscii = enc == 11; // RTL_TEXTENCODING_ASCII_US
                auto const encIsUtf8 = enc == 76; // RTL_TEXTENCODING_UTF8
                if (!compat::EvaluateAsInt(expr->getArg(3),
                        res, compiler.getASTContext())
                    || res != 0x333) // OSTRING_TO_OUSTRING_CVTFLAGS
                {
                    return true;
                }
                if (!encIsAscii && cont == ContentKind::Ascii) {
                    report(
                        DiagnosticsEngine::Warning,
                        ("suspicious 'rtl::OUString' constructor with text"
                         " encoding %0 but plain ASCII content; use"
                         " 'RTL_TEXTENCODING_ASCII_US' instead"),
                        expr->getArg(2)->getExprLoc())
                        << compat::toString(enc, 10) << expr->getSourceRange();
                    return true;
                }
                if (encIsUtf8) {
                    if (cont == ContentKind::Arbitrary) {
                        report(
                            DiagnosticsEngine::Warning,
                            ("suspicious 'rtl::OUString' constructor with text"
                             " encoding 'RTL_TEXTENCODING_UTF8' but non-UTF-8"
                             " content"),
                            expr->getArg(0)->getExprLoc())
                            << expr->getSourceRange();
                    } else {
                        assert(cont == ContentKind::Utf8);
                        //TODO: keep original content as much as possible
                        std::ostringstream s;
                        for (auto const c: utf8Cont) {
                            if (c == '\\') {
                                s << "\\\\";
                            } else if (c == '"') {
                                s << "\\\"";
                            } else if (c == '\a') {
                                s << "\\a";
                            } else if (c == '\b') {
                                s << "\\b";
                            } else if (c == '\f') {
                                s << "\\f";
                            } else if (c == '\n') {
                                s << "\\n";
                            } else if (c == '\r') {
                                s << "\\r";
                            } else if (c == '\t') {
                                s << "\\r";
                            } else if (c == '\v') {
                                s << "\\v";
                            } else if (c <= 0x1F || c == 0x7F) {
                                s << "\\x" << std::oct << std::setw(3)
                                  << std::setfill('0')
                                  << static_cast<std::uint_least32_t>(c);
                            } else if (c < 0x7F) {
                                s << char(c);
                            } else if (c <= 0xFFFF) {
                                s << "\\u" << std::hex << std::uppercase
                                  << std::setw(4) << std::setfill('0')
                                  << static_cast<std::uint_least32_t>(c);
                            } else {
                                assert(c <= 0x10FFFF);
                                s << "\\U" << std::hex << std::uppercase
                                  << std::setw(8) << std::setfill('0')
                                  << static_cast<std::uint_least32_t>(c);
                            }
                        }
                        report(
                            DiagnosticsEngine::Warning,
                            ("simplify construction of %0 with UTF-8 content as"
                             " OUString(u\"%1\")"),
                            expr->getExprLoc())
                            << classdecl << s.str() << expr->getSourceRange();

                    }
                    return true;
                }
                if (cont != ContentKind::Ascii || emb) {
                    // cf. remaining uses of RTL_CONSTASCII_USTRINGPARAM
                    return true;
                }
                kind = ChangeKind::Char;
                pass = n == 0
                    ? PassThrough::EmptyConstantString
                    : PassThrough::NonEmptyConstantString;
                simplify = true;
                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)->getSubExpr()
                        ->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;
                        }
                        loplugin::DeclCheck dc(fdecl);
                        if (pass == PassThrough::EmptyConstantString) {
                            if ((dc.Function("equals").Class("OUString")
                                 .Namespace("rtl").GlobalNamespace())
                                || (dc.Operator(OO_EqualEqual).Namespace("rtl")
                                    .GlobalNamespace()))
                            {
                                report(
                                    DiagnosticsEngine::Warning,
                                    ("rewrite call of '%0' with construction of"
                                     " %1 with empty string constant argument"
                                     " as call of 'rtl::OUString::isEmpty'"),
                                    getMemberLocation(call))
                                    << fdecl->getQualifiedNameAsString()
                                    << classdecl << call->getSourceRange();
                                return true;
                            }
                            if (dc.Operator(OO_ExclaimEqual).Namespace("rtl")
                                    .GlobalNamespace())
                            {
                                report(
                                    DiagnosticsEngine::Warning,
                                    ("rewrite call of '%0' with construction of"
                                     " %1 with empty string constant argument"
                                     " as call of '!rtl::OUString::isEmpty'"),
                                    getMemberLocation(call))
                                    << fdecl->getQualifiedNameAsString()
                                    << classdecl << call->getSourceRange();
                                return true;
                            }
                            if ((dc.Operator(OO_Plus).Namespace("rtl")
                                    .GlobalNamespace())
                                || (dc.Operator(OO_Plus).Class("OUString")
                                    .Namespace("rtl").GlobalNamespace()))
                            {
                                report(
                                    DiagnosticsEngine::Warning,
                                    ("call of '%0' with suspicious construction"
                                     " of %1 with empty string constant"
                                     " argument"),
                                    getMemberLocation(call))
                                    << fdecl->getQualifiedNameAsString()
                                    << classdecl << call->getSourceRange();
                                return true;
                            }
                            if (dc.Operator(OO_Equal).Class("OUString")
                                .Namespace("rtl").GlobalNamespace())
                            {
                                report(
                                    DiagnosticsEngine::Warning,
                                    ("rewrite call of '%0' with construction of"
                                     " %1 with empty string constant argument"
                                     " as call of 'rtl::OUString::clear'"),
                                    getMemberLocation(call))
                                    << fdecl->getQualifiedNameAsString()
                                    << classdecl << call->getSourceRange();
                                return true;
                            }
                        } else {
                            assert(pass == PassThrough::NonEmptyConstantString);
                            if (dc.Function("equals").Class("OUString")
                                .Namespace("rtl").GlobalNamespace())
                            {
                                report(
                                    DiagnosticsEngine::Warning,
                                    ("rewrite call of '%0' with construction of"
                                     " %1 with %2 as 'operator =='"),
                                    getMemberLocation(call))
                                    << fdecl->getQualifiedNameAsString()
                                    << classdecl << describeChangeKind(kind)
                                    << call->getSourceRange();
                                return true;
                            }
                            if ((dc.Operator(OO_Plus).Namespace("rtl")
                                    .GlobalNamespace())
                                || (dc.Operator(OO_Plus).Class("OUString")
                                    .Namespace("rtl").GlobalNamespace())
                                || (dc.Operator(OO_EqualEqual).Namespace("rtl")
                                    .GlobalNamespace())
                                || (dc.Operator(OO_ExclaimEqual)
                                    .Namespace("rtl").GlobalNamespace()))
                            {
                                if (dc.Operator(OO_Plus).Namespace("rtl")
                                    .GlobalNamespace())
                                {
                                    auto file = getFilenameOfLocation(
                                            compiler.getSourceManager()
                                            .getSpellingLoc(
                                                expr->getBeginLoc()));
                                    if (loplugin::isSamePathname(
                                            file,
                                            (SRCDIR
                                             "/sal/qa/rtl/strings/test_ostring_concat.cxx"))
                                        || loplugin::isSamePathname(
                                            file,
                                            (SRCDIR
                                             "/sal/qa/rtl/strings/test_oustring_concat.cxx")))
                                    {
                                        return true;
                                    }
                                }
                                auto loc = expr->getArg(0)->getBeginLoc();
                                while (compiler.getSourceManager()
                                       .isMacroArgExpansion(loc))
                                {
                                    loc = compiler.getSourceManager()
                                        .getImmediateMacroCallerLoc(loc);
                                }
                                if (kind == ChangeKind::SingleChar) {
                                    report(
                                        DiagnosticsEngine::Warning,
                                        ("rewrite construction of %0 with %1 in"
                                         " call of '%2' as construction of"
                                         " 'OUStringChar'"),
                                        getMemberLocation(expr))
                                        << classdecl << describeChangeKind(kind)
                                        << fdecl->getQualifiedNameAsString()
                                        << expr->getSourceRange();
                                } else {
                                    report(
                                        DiagnosticsEngine::Warning,
                                        ("elide construction of %0 with %1 in"
                                         " call of '%2'"),
                                        getMemberLocation(expr))
                                        << classdecl << describeChangeKind(kind)
                                        << fdecl->getQualifiedNameAsString()
                                        << expr->getSourceRange();
                                }
                                return true;
                            }
                        }
                    } else if (isa<CXXConstructExpr>(call)) {
                    } else {
                        assert(false);
                    }
                }
            }
        }
        if (simplify) {
            report(
                DiagnosticsEngine::Warning,
                "simplify construction of %0 with %1", expr->getExprLoc())
                << classdecl << describeChangeKind(kind)
                << expr->getSourceRange();
        }
        return true;
    }

    auto consDecl = expr->getConstructor();
    for (unsigned i = 0; i != consDecl->getNumParams(); ++i) {
        auto t = consDecl->getParamDecl(i)->getType();
        if (loplugin::TypeCheck(t).NotSubstTemplateTypeParmType()
            .LvalueReference().Const().NotSubstTemplateTypeParmType()
            .Class("OUString").Namespace("rtl").GlobalNamespace())
        {
            auto argExpr = expr->getArg(i);
            if (argExpr && i <= consDecl->getNumParams())
            {
                if (!hasOverloads(consDecl, expr->getNumArgs()))
                {
                    handleOUStringCtor(expr, argExpr, consDecl, true);
                }
            }
        }
        if (loplugin::TypeCheck(t).NotSubstTemplateTypeParmType()
            .LvalueReference().Const().NotSubstTemplateTypeParmType()
            .Class("OString").Namespace("rtl").GlobalNamespace())
        {
            auto argExpr = expr->getArg(i);
            if (argExpr && i <= consDecl->getNumParams())
            {
                if (!hasOverloads(consDecl, expr->getNumArgs()))
                {
                    handleOStringCtor(expr, argExpr, consDecl, true);
                }
            }
        }
    }

    return true;
}

bool StringConstant::VisitReturnStmt(ReturnStmt const * stmt) {
    if (ignoreLocation(stmt)) {
        return true;
    }
    auto const e1 = stmt->getRetValue();
    if (e1 == nullptr) {
        return true;
    }
    auto const tc1 = loplugin::TypeCheck(e1->getType().getTypePtr());
    if (!(tc1.Class("OString").Namespace("rtl").GlobalNamespace()
          || tc1.Class("OUString").Namespace("rtl").GlobalNamespace()))
    {
        return true;
    }
    assert(!returnTypes_.empty());
    auto const tc2 = loplugin::TypeCheck(returnTypes_.top().getTypePtr());
    if (!(tc2.Class("OString").Namespace("rtl").GlobalNamespace()
          || tc2.Class("OUString").Namespace("rtl").GlobalNamespace()))
    {
        return true;
    }
    auto const e2 = dyn_cast<CXXFunctionalCastExpr>(e1->IgnoreImplicit());
    if (e2 == nullptr) {
        return true;
    }
    auto const e3 = dyn_cast<CXXBindTemporaryExpr>(e2->getSubExpr());
    if (e3 == nullptr) {
        return true;
    }
    auto const e4 = dyn_cast<CXXConstructExpr>(e3->getSubExpr());
    if (e4 == nullptr) {
        return true;
    }
    if (e4->getNumArgs() != 2) {
        return true;
    }
    auto const t = e4->getArg(0)->getType();
    if (!(t.isConstQualified() && t->isConstantArrayType())) {
        return true;
    }
    auto const e5 = e4->getArg(1);
    if (!(isa<CXXDefaultArgExpr>(e5)
          && (loplugin::TypeCheck(e5->getType()).Struct("Dummy").Namespace("libreoffice_internal")
              .Namespace("rtl").GlobalNamespace())))
    {
        return true;
    }
    report(DiagnosticsEngine::Warning, "elide constructor call", e1->getBeginLoc())
        << e1->getSourceRange();
    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 "sal_Unicode argument";
    case ChangeKind::OUStringChar:
        return "OUStringChar argument";
    }
    llvm_unreachable("unknown change kind");
}

bool StringConstant::isStringConstant(
    Expr const * expr, unsigned * size, bool * nonArray, ContentKind * content,
    bool * embeddedNuls, bool * terminatingNul,
    std::vector<char32_t> * utf8Content)
{
    assert(expr != nullptr);
    assert(size != nullptr);
    assert(nonArray != nullptr);
    assert(content != nullptr);
    assert(embeddedNuls != nullptr);
    assert(terminatingNul != nullptr);
    QualType t = expr->getType();
    // Look inside RTL_CONSTASCII_STRINGPARAM:
    if (loplugin::TypeCheck(t).Pointer().Const().Char()) {
        auto e2 = dyn_cast<UnaryOperator>(expr);
        if (e2 != nullptr && e2->getOpcode() == UO_AddrOf) {
            auto e3 = dyn_cast<ArraySubscriptExpr>(
                e2->getSubExpr()->IgnoreParenImpCasts());
            if (e3 == nullptr || !isZero(e3->getIdx()->IgnoreParenImpCasts())) {
                return false;
            }
            expr = e3->getBase()->IgnoreParenImpCasts();
            t = expr->getType();
        }
    }
    if (!t.isConstQualified()) {
        return false;
    }
    DeclRefExpr const * dre = dyn_cast<DeclRefExpr>(expr);
    if (dre != nullptr) {
        VarDecl const * var = dyn_cast<VarDecl>(dre->getDecl());
        if (var != nullptr) {
            Expr const * init = var->getAnyInitializer();
            if (init != nullptr) {
                expr = init->IgnoreParenImpCasts();
            }
        }
    }
    bool isPtr;
    if (loplugin::TypeCheck(t).Pointer().Const().Char()) {
        isPtr = true;
    } else if (t->isConstantArrayType()
               && (loplugin::TypeCheck(
                       t->getAsArrayTypeUnsafe()->getElementType())
                   .Char()))
    {
        isPtr = false;
    } else {
        return false;
    }
    clang::StringLiteral const * lit = dyn_cast<clang::StringLiteral>(expr);
    if (lit != nullptr) {
        if (!(compat::isOrdinary(lit) || lit->isUTF8())) {
            return false;
        }
        unsigned n = lit->getLength();
        ContentKind cont = ContentKind::Ascii;
        bool emb = false;
        char32_t val = 0;
        enum class Utf8State { Start, E0, EB, F0, F4, Trail1, Trail2, Trail3 };
        Utf8State s = Utf8State::Start;
        StringRef str = lit->getString();
        for (unsigned i = 0; i != n; ++i) {
            auto const c = static_cast<unsigned char>(str[i]);
            if (c == '\0') {
                emb = true;
            }
            switch (s) {
            case Utf8State::Start:
                if (c >= 0x80) {
                    if (c >= 0xC2 && c <= 0xDF) {
                        val = c & 0x1F;
                        s = Utf8State::Trail1;
                    } else if (c == 0xE0) {
                        val = c & 0x0F;
                        s = Utf8State::E0;
                    } else if ((c >= 0xE1 && c <= 0xEA)
                               || (c >= 0xEE && c <= 0xEF))
                    {
                        val = c & 0x0F;
                        s = Utf8State::Trail2;
                    } else if (c == 0xEB) {
                        val = c & 0x0F;
                        s = Utf8State::EB;
                    } else if (c == 0xF0) {
                        val = c & 0x03;
                        s = Utf8State::F0;
                    } else if (c >= 0xF1 && c <= 0xF3) {
                        val = c & 0x03;
                        s = Utf8State::Trail3;
                    } else if (c == 0xF4) {
                        val = c & 0x03;
                        s = Utf8State::F4;
                    } else {
                        cont = ContentKind::Arbitrary;
                    }
                } else if (utf8Content != nullptr
                           && cont != ContentKind::Arbitrary)
                {
                    utf8Content->push_back(c);
                }
                break;
            case Utf8State::E0:
                if (c >= 0xA0 && c <= 0xBF) {
                    val = (val << 6) | (c & 0x3F);
                    s = Utf8State::Trail1;
                } else {
                    cont = ContentKind::Arbitrary;
                    s = Utf8State::Start;
                }
                break;
            case Utf8State::EB:
                if (c >= 0x80 && c <= 0x9F) {
                    val = (val << 6) | (c & 0x3F);
                    s = Utf8State::Trail1;
                } else {
                    cont = ContentKind::Arbitrary;
                    s = Utf8State::Start;
                }
                break;
            case Utf8State::F0:
                if (c >= 0x90 && c <= 0xBF) {
                    val = (val << 6) | (c & 0x3F);
                    s = Utf8State::Trail2;
                } else {
                    cont = ContentKind::Arbitrary;
                    s = Utf8State::Start;
                }
                break;
            case Utf8State::F4:
                if (c >= 0x80 && c <= 0x8F) {
                    val = (val << 6) | (c & 0x3F);
                    s = Utf8State::Trail2;
                } else {
                    cont = ContentKind::Arbitrary;
                    s = Utf8State::Start;
                }
                break;
            case Utf8State::Trail1:
                if (c >= 0x80 && c <= 0xBF) {
                    cont = ContentKind::Utf8;
                    if (utf8Content != nullptr)
                    {
                        utf8Content->push_back((val << 6) | (c & 0x3F));
                        val = 0;
                    }
                } else {
                    cont = ContentKind::Arbitrary;
                }
                s = Utf8State::Start;
                break;
            case Utf8State::Trail2:
                if (c >= 0x80 && c <= 0xBF) {
                    val = (val << 6) | (c & 0x3F);
                    s = Utf8State::Trail1;
                } else {
                    cont = ContentKind::Arbitrary;
                    s = Utf8State::Start;
                }
                break;
            case Utf8State::Trail3:
                if (c >= 0x80 && c <= 0xBF) {
                    val = (val << 6) | (c & 0x3F);
                    s = Utf8State::Trail2;
                } else {
                    cont = ContentKind::Arbitrary;
                    s = Utf8State::Start;
                }
                break;
            }
        }
        *size = n;
        *nonArray = isPtr;
        *content = cont;
        *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 *>();
            if (e == nullptr) {
                return false;
            }
            if (!v.getLValueOffset().isZero()) {
                return false//TODO
            }
            Expr const * e2 = e->IgnoreParenImpCasts();
            if (e2 != e) {
                return isStringConstant(
                    e2, size, nonArray, content, 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());
            *nonArray = isPtr || *nonArray;
            *content = ContentKind::Ascii; //TODO
            *embeddedNuls = false//TODO
            *terminatingNul = true;
            return true;
        }
    case APValue::Array:
        {
            if (v.hasArrayFiller()) { //TODO: handle final NULL filler?
                return false;
            }
            unsigned n = v.getArraySize();
            assert(n != 0);
            ContentKind cont = ContentKind::Ascii;
            bool emb = false;
            //TODO: check for ContentType::Utf8
            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)) {
                    cont = ContentKind::Arbitrary;
                }
            }
            APValue e(v.getArrayInitializedElt(n - 1));
            if (!e.isInt()) { //TODO: assert?
                return false;
            }
            bool trm = e.getInt() == 0;
            *size = trm ? n - 1 : n;
            *nonArray = isPtr;
            *content = cont;
            *embeddedNuls = emb;
            *terminatingNul = trm;
            return true;
        }
    default:
        assert(false); //TODO???
        return false;
    }
}

bool StringConstant::isZero(Expr const * expr) {
    APSInt res;
    return compat::EvaluateAsInt(expr, res, compiler.getASTContext()) && res == 0;
}

void StringConstant::reportChange(
    Expr const * expr, ChangeKind kind, std::string const & original,
    std::string const & replacement, PassThrough pass, bool nonArray,
    char const * rewriteFrom, char const * rewriteTo)
{
    assert((rewriteFrom == nullptr) == (rewriteTo == nullptr));
    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;
                    }
                    loplugin::DeclCheck dc(fdecl);
                    if (pass == PassThrough::EmptyConstantString) {
                        if ((dc.Function("equals").Class("OUString")
                             .Namespace("rtl").GlobalNamespace())
                            || (dc.Operator(OO_EqualEqual).Namespace("rtl")
                                .GlobalNamespace()))
                        {
                            report(
                                DiagnosticsEngine::Warning,
                                ("rewrite call of '%0' with call of %1 with"
                                 " empty string constant argument as call of"
                                 " 'rtl::OUString::isEmpty'"),
                                getMemberLocation(call))
                                << fdecl->getQualifiedNameAsString() << original
                                << call->getSourceRange();
                            return;
                        }
                        if (dc.Operator(OO_ExclaimEqual).Namespace("rtl")
                            .GlobalNamespace())
                        {
                            report(
                                DiagnosticsEngine::Warning,
                                ("rewrite call of '%0' with call of %1 with"
                                 " empty string constant argument as call of"
                                 " '!rtl::OUString::isEmpty'"),
--> --------------------

--> maximum size reached

--> --------------------

98%


¤ Dauer der Verarbeitung: 0.22 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.