/* -*- 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/.
*/
// 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"
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::VisitCXXMemberCallExpr(CXXMemberCallExpr const * expr) { if (ignoreLocation(expr)) { returntrue;
}
FunctionDecl const * fdecl = expr->getDirectCallee(); if (fdecl == nullptr) { returntrue;
} autoconst c = loplugin::DeclCheck(fdecl).Function("getStr"); if ((c.Class("OString").Namespace("rtl").GlobalNamespace()
|| c.Class("OUString").Namespace("rtl").GlobalNamespace())
&& fdecl->getNumParams() == 0)
{ autoconst e1 = expr->getImplicitObjectArgument()->IgnoreImplicit()->IgnoreParens(); if (autoconst e2 = dyn_cast<CXXTemporaryObjectExpr>(e1)) { if (e2->getNumArgs() != 0) { returntrue;
}
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(); returntrue;
} if (autoconst e2 = dyn_cast<CXXFunctionalCastExpr>(e1)) { autoconst e3 = dyn_cast<clang::StringLiteral>(e2->getSubExprAsWritten()->IgnoreParens()); if (e3 == nullptr) { returntrue;
}
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(); returntrue;
}
} returntrue;
}
bool StringConstant::VisitCXXConstructExpr(CXXConstructExpr const * expr) { if (ignoreLocation(expr)) { returntrue;
} 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())
{ returntrue;
}
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))
{ returntrue;
} 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))
{ returntrue;
}
APSInt res; if (!compat::EvaluateAsInt(expr->getArg(1),
res, compiler.getASTContext()))
{ returntrue;
} 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(); returntrue;
}
APSInt enc; if (!compat::EvaluateAsInt(expr->getArg(2),
enc, compiler.getASTContext()))
{ returntrue;
} autoconst encIsAscii = enc == 11; // RTL_TEXTENCODING_ASCII_US autoconst encIsUtf8 = enc == 76; // RTL_TEXTENCODING_UTF8 if (!compat::EvaluateAsInt(expr->getArg(3),
res, compiler.getASTContext())
|| res != 0x333) // OSTRING_TO_OUSTRING_CVTFLAGS
{ returntrue;
} 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(); returntrue;
} 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 (autoconst c: utf8Cont) { if (c == '\\') {
s << "\\\\";
} elseif (c == '"') {
s << "\\\"";
} elseif (c == '\a') {
s << "\\a";
} elseif (c == '\b') {
s << "\\b";
} elseif (c == '\f') {
s << "\\f";
} elseif (c == '\n') {
s << "\\n";
} elseif (c == '\r') {
s << "\\r";
} elseif (c == '\t') {
s << "\\r";
} elseif (c == '\v') {
s << "\\v";
} elseif (c <= 0x1F || c == 0x7F) {
s << "\\x" << std::oct << std::setw(3)
<< std::setfill('0')
<< static_cast<std::uint_least32_t>(c);
} elseif (c < 0x7F) {
s << char(c);
} elseif (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();
} returntrue;
} if (cont != ContentKind::Ascii || emb) { // cf. remaining uses of RTL_CONSTASCII_USTRINGPARAM returntrue;
}
kind = ChangeKind::Char;
pass = n == 0
? PassThrough::EmptyConstantString
: PassThrough::NonEmptyConstantString;
simplify = true; break;
} default: returntrue;
} 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();
} elseif (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(); returntrue;
} 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(); returntrue;
} 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(); returntrue;
} 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(); returntrue;
}
} 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(); returntrue;
} 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")))
{ returntrue;
}
} 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();
} returntrue;
}
}
} elseif (isa<CXXConstructExpr>(call)) {
} else {
assert(false);
}
}
}
} if (simplify) {
report(
DiagnosticsEngine::Warning, "simplify construction of %0 with %1", expr->getExprLoc())
<< classdecl << describeChangeKind(kind)
<< expr->getSourceRange();
} returntrue;
}
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);
}
}
}
}
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.