/* -*- 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/.
*/ #ifndef LO_CLANG_SHARED_PLUGINS
/** Look for places where we are making a substring copy of an OUString, and then passing it to a function that takes a u16string_view, in which case it is more efficient to pass a view of the OUString, rather than making a copy.
TODO currently does not check if there is some other visible overload of the callee, that can take a string_view. TODO handle OUStringBuffer/OStringBuffer similarly
*/
namespace
{ class StringView : public loplugin::FilteringPlugin<StringView>
{ public: explicit StringView(loplugin::InstantiationData const& data)
: FilteringPlugin(data)
{
}
void StringView::handleCXXConstructExpr(CXXConstructExpr const* expr)
{
QualType argType; enum
{
None,
OrChar,
ViaConcatenation
} extra
= None; autoconst d = expr->getConstructor(); switch (d->getNumParams())
{ case 0: break; case 1:
{ autoconst t = d->getParamDecl(0)->getType(); if (t->isAnyCharacterType())
{
argType = expr->getArg(0)->IgnoreImplicit()->getType();
extra = OrChar; break;
}
loplugin::TypeCheck tc(t); if (tc.RvalueReference().Struct("StringNumber").Namespace("rtl").GlobalNamespace()
|| tc.ClassOrStruct("basic_string_view").StdNamespace())
{
argType = expr->getArg(0)->IgnoreImplicit()->getType(); break;
} if (tc.RvalueReference().Struct("StringConcat").Namespace("rtl").GlobalNamespace())
{
argType = expr->getArg(0)->IgnoreImplicit()->getType();
extra = ViaConcatenation; break;
} return;
} case 2:
{ autoconst t0 = d->getParamDecl(0)->getType(); if (t0->isPointerType() && t0->getPointeeType()->isAnyCharacterType())
{ autoconst t = d->getParamDecl(1)->getType(); if (t->isIntegralType(compiler.getASTContext())
&& !(t->isBooleanType() || t->isAnyCharacterType()))
{ autoconst arg = expr->getArg(1); if (!arg->isValueDependent())
{ if (autoconst val = arg->getIntegerConstantExpr(compiler.getASTContext()))
{ if (val->getExtValue() == 1)
{
extra = OrChar;
}
}
}
argType = expr->getArg(0)->IgnoreImplicit()->getType(); break;
}
} if (loplugin::TypeCheck(d->getParamDecl(1)->getType())
.Struct("Dummy")
.Namespace("libreoffice_internal")
.Namespace("rtl")
.GlobalNamespace())
{
argType = expr->getArg(0)->IgnoreImplicit()->getType(); break;
} return;
} default: return;
}
report(DiagnosticsEngine::Warning, "instead of an %0%select{| constructed from a %2}1, pass a" " '%select{std::string_view|std::u16string_view}3'" "%select{| (or an '%select{rtl::OStringChar|rtl::OUStringChar}3')|" " via 'rtl::Concat2View'}4",
expr->getExprLoc())
<< expr->getType() << (argType.isNull() ? 0 : 1) << argType
<< (loplugin::TypeCheck(expr->getType()).Class("OString").Namespace("rtl").GlobalNamespace()
? 0
: 1)
<< extra << expr->getSourceRange();
}
void StringView::handleCXXMemberCallExpr(CXXMemberCallExpr const* expr)
{ autoconst dc1 = loplugin::DeclCheck(expr->getMethodDecl()); if (autoconst dc2 = dc1.Function("copy"))
{ if (dc2.Class("OString").Namespace("rtl").GlobalNamespace()
|| dc2.Class("OUString").Namespace("rtl").GlobalNamespace()
|| dc2.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace())
{
report(DiagnosticsEngine::Warning, "rather than copy, pass with a view using subView()",
expr->getExprLoc())
<< expr->getSourceRange();
} return;
} if (autoconst dc2 = dc1.Function("getToken"))
{ if (dc2.Class("OString").Namespace("rtl").GlobalNamespace()
|| dc2.Class("OUString").Namespace("rtl").GlobalNamespace()
|| dc2.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace())
{
report(DiagnosticsEngine::Warning, "rather than getToken, pass with a view using o3tl::getToken()",
expr->getExprLoc())
<< expr->getSourceRange();
} return;
} if (autoconst dc2 = dc1.Function("trim"))
{ if (dc2.Class("OString").Namespace("rtl").GlobalNamespace()
|| dc2.Class("OUString").Namespace("rtl").GlobalNamespace()
|| dc2.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace())
{
report(DiagnosticsEngine::Warning, "rather than trim, pass with a view using o3tl::trim()", expr->getExprLoc())
<< expr->getSourceRange();
} return;
} if (autoconst dc2 = dc1.Function("makeStringAndClear"))
{ if (dc2.Class("OStringBuffer").Namespace("rtl").GlobalNamespace()
|| dc2.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace())
{ autoconst obj = expr->getImplicitObjectArgument(); if (!(obj->isLValue() || obj->getType()->isPointerType()))
{
report(DiagnosticsEngine::Warning, "rather than call makeStringAndClear on an rvalue, pass with a view",
expr->getExprLoc())
<< expr->getSourceRange();
}
} return;
} if (autoconst dc2 = dc1.Function("toString"))
{ if (dc2.Class("OStringBuffer").Namespace("rtl").GlobalNamespace()
|| dc2.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace())
{
report(DiagnosticsEngine::Warning, "rather than call toString, pass with a view",
expr->getExprLoc())
<< expr->getSourceRange();
} return;
}
}
bool StringView::VisitCXXMemberCallExpr(CXXMemberCallExpr const* expr)
{ if (ignoreLocation(expr))
{ returntrue;
} /** check for calls to O[U]StringBuffer::append that could be passed as a
std::u16string_view */ if (loplugin::TypeCheck(expr->getType())
.Class("OUStringBuffer")
.Namespace("rtl")
.GlobalNamespace()
|| loplugin::TypeCheck(expr->getType())
.Class("OStringBuffer")
.Namespace("rtl")
.GlobalNamespace())
{ autoconst dc = loplugin::DeclCheck(expr->getMethodDecl()); if (dc.Function("append") || dc.Function("indexOf") || dc.Function("lastIndexOf"))
{
handleSubExprThatCouldBeView(expr->getArg(0));
} elseif (dc.Function("insert"))
{
handleSubExprThatCouldBeView(expr->getArg(1));
}
}
// rather than getToken...toInt32, use o3tl::toInt(o3tl::getToken(...) auto tc = loplugin::TypeCheck(expr->getImplicitObjectArgument()->getType()); if (tc.Class("OUString").Namespace("rtl").GlobalNamespace()
|| tc.Class("OString").Namespace("rtl").GlobalNamespace())
{ autoconst dc = loplugin::DeclCheck(expr->getMethodDecl()); if (dc.Function("toInt32") || dc.Function("toUInt32") || dc.Function("toInt64")
|| dc.Function("toDouble") || dc.Function("equalsAscii") || dc.Function("equalsAsciiL")
|| dc.Function("equalsIgnoreAsciiCase") || dc.Function("compareToIgnoreAsciiCase")
|| dc.Function("matchIgnoreAsciiCase") || dc.Function("trim")
|| dc.Function("startsWith") || dc.Function("endsWith") || dc.Function("match")
|| dc.Function("isEmpty") || dc.Function("getLength")
|| dc.Function("iterateCodePoints"))
{
handleSubExprThatCouldBeView(expr->getImplicitObjectArgument());
}
} returntrue;
}
/** check for calls to O[U]StringBuffer constructor that could be passed as a
std::u16string_view */ bool StringView::VisitCXXConstructExpr(CXXConstructExpr const* expr)
{ if (ignoreLocation(expr))
{ returntrue;
} if (!loplugin::TypeCheck(expr->getType())
.Class("OUStringBuffer")
.Namespace("rtl")
.GlobalNamespace()
&& !loplugin::TypeCheck(expr->getType())
.Class("OStringBuffer")
.Namespace("rtl")
.GlobalNamespace())
{ returntrue;
} if (!compiler.getLangOpts().CPlusPlus17 && expr->isElidable()) // external C++03 code
{ returntrue;
} if (expr->getNumArgs() > 0)
handleSubExprThatCouldBeView(expr->getArg(0)); returntrue;
}
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.