diff options
Diffstat (limited to 'compilerplugins/clang/stringview.cxx')
-rw-r--r-- | compilerplugins/clang/stringview.cxx | 268 |
1 files changed, 219 insertions, 49 deletions
diff --git a/compilerplugins/clang/stringview.cxx b/compilerplugins/clang/stringview.cxx index abfc87f78fd6..9484f3ace957 100644 --- a/compilerplugins/clang/stringview.cxx +++ b/compilerplugins/clang/stringview.cxx @@ -16,6 +16,7 @@ #include "plugin.hxx" #include "check.hxx" +#include "config_clang.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/StmtVisitor.h" @@ -39,7 +40,13 @@ public: { } - bool preRun() override { return true; } + bool preRun() override + { + auto const fn = handler.getMainFileName(); + return !(loplugin::isSamePathname(fn, SRCDIR "/sal/qa/OStringBuffer/rtl_OStringBuffer.cxx") + || loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/qa/rtl/strings/") + || loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/qa/rtl/oustring/")); + } virtual void run() override { @@ -51,8 +58,11 @@ public: bool VisitFunctionDecl(FunctionDecl const*); bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr const*); bool VisitImplicitCastExpr(ImplicitCastExpr const*); + bool VisitCXXMemberCallExpr(CXXMemberCallExpr const*); + bool VisitCXXConstructExpr(CXXConstructExpr const*); private: + void handleSubExprThatCouldBeView(Expr const* expr); void handleCXXConstructExpr(CXXConstructExpr const* expr); void handleCXXMemberCallExpr(CXXMemberCallExpr const* expr); }; @@ -62,32 +72,35 @@ bool StringView::VisitCXXOperatorCallExpr(CXXOperatorCallExpr const* cxxOperator if (ignoreLocation(cxxOperatorCallExpr)) return true; - auto check = [&](const Expr* expr) -> void { - auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(compat::IgnoreImplicit(expr)); - if (!memberCallExpr) - return; - auto methodDecl = memberCallExpr->getMethodDecl(); - if (!methodDecl->getIdentifier() || methodDecl->getName() != "copy") - return; - report(DiagnosticsEngine::Warning, "rather than copy, pass with a view using subView()", - compat::getBeginLoc(expr)) - << expr->getSourceRange(); - }; auto op = cxxOperatorCallExpr->getOperator(); if (op == OO_Plus && cxxOperatorCallExpr->getNumArgs() == 2) { - check(cxxOperatorCallExpr->getArg(0)); - check(cxxOperatorCallExpr->getArg(1)); + handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(0)); + handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(1)); } - if (compat::isComparisonOp(cxxOperatorCallExpr)) + if (cxxOperatorCallExpr->isComparisonOp()) { - check(cxxOperatorCallExpr->getArg(0)); - check(cxxOperatorCallExpr->getArg(1)); + handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(0)); + handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(1)); } else if (op == OO_PlusEqual) - check(cxxOperatorCallExpr->getArg(1)); + handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(1)); else if (op == OO_Subscript) - check(cxxOperatorCallExpr->getArg(0)); + handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(0)); + else if (op == OO_Equal) + { + if (loplugin::TypeCheck(cxxOperatorCallExpr->getType()) + .Class("OUStringBuffer") + .Namespace("rtl") + .GlobalNamespace() + || loplugin::TypeCheck(cxxOperatorCallExpr->getType()) + .Class("OStringBuffer") + .Namespace("rtl") + .GlobalNamespace()) + { + handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(1)); + } + } return true; } @@ -111,16 +124,27 @@ bool StringView::VisitImplicitCastExpr(ImplicitCastExpr const* expr) { return true; } - auto const e = expr->getSubExprAsWritten()->IgnoreParens(); + handleSubExprThatCouldBeView(expr->getSubExprAsWritten()); + return true; +} + +void StringView::handleSubExprThatCouldBeView(Expr const* subExpr) +{ + auto const e0 = subExpr->IgnoreImplicit(); + auto const e = e0->IgnoreParens(); auto const tc = loplugin::TypeCheck(e->getType()); if (!(tc.Class("OString").Namespace("rtl").GlobalNamespace() - || tc.Class("OUString").Namespace("rtl").GlobalNamespace())) + || tc.Class("OUString").Namespace("rtl").GlobalNamespace() + || tc.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace())) { - return true; + return; } if (auto const e1 = dyn_cast<CXXConstructExpr>(e)) { - handleCXXConstructExpr(e1); + if (e0 == subExpr) + { + handleCXXConstructExpr(e1); + } } else if (auto const e2 = dyn_cast<CXXFunctionalCastExpr>(e)) { @@ -138,12 +162,18 @@ bool StringView::VisitImplicitCastExpr(ImplicitCastExpr const* expr) { handleCXXMemberCallExpr(e3); } - return true; } void StringView::handleCXXConstructExpr(CXXConstructExpr const* expr) { - bool charArg = false; + QualType argType; + enum + { + None, + OrChar, + ViaConcatenation + } extra + = None; auto const d = expr->getConstructor(); switch (d->getNumParams()) { @@ -154,24 +184,21 @@ void StringView::handleCXXConstructExpr(CXXConstructExpr const* expr) auto const t = d->getParamDecl(0)->getType(); if (t->isAnyCharacterType()) { - charArg = true; + argType = expr->getArg(0)->IgnoreImplicit()->getType(); + extra = OrChar; break; } loplugin::TypeCheck tc(t); - if (tc.LvalueReference() - .Const() - .Class("OStringLiteral") - .Namespace("rtl") - .GlobalNamespace() - || tc.LvalueReference() - .Const() - .Class("OUStringLiteral") - .Namespace("rtl") - .GlobalNamespace() - || tc.RvalueReference().Struct("OStringNumber").Namespace("rtl").GlobalNamespace() - || tc.RvalueReference().Struct("OUStringNumber").Namespace("rtl").GlobalNamespace() + 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; @@ -185,6 +212,18 @@ void StringView::handleCXXConstructExpr(CXXConstructExpr const* expr) if (t->isIntegralType(compiler.getASTContext()) && !(t->isBooleanType() || t->isAnyCharacterType())) { + auto const arg = expr->getArg(1); + if (!arg->isValueDependent()) + { + if (auto const val = arg->getIntegerConstantExpr(compiler.getASTContext())) + { + if (val->getExtValue() == 1) + { + extra = OrChar; + } + } + } + argType = expr->getArg(0)->IgnoreImplicit()->getType(); break; } } @@ -194,6 +233,7 @@ void StringView::handleCXXConstructExpr(CXXConstructExpr const* expr) .Namespace("rtl") .GlobalNamespace()) { + argType = expr->getArg(0)->IgnoreImplicit()->getType(); break; } return; @@ -202,31 +242,161 @@ void StringView::handleCXXConstructExpr(CXXConstructExpr const* expr) return; } report(DiagnosticsEngine::Warning, - "instead of an %0, pass a '%select{std::string_view|std::u16string_view}1'" - "%select{| (or an '%select{rtl::OStringChar|rtl::OUStringChar}1')}2", + "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() + << expr->getType() << (argType.isNull() ? 0 : 1) << argType << (loplugin::TypeCheck(expr->getType()).Class("OString").Namespace("rtl").GlobalNamespace() ? 0 : 1) - << charArg << expr->getSourceRange(); + << extra << expr->getSourceRange(); } void StringView::handleCXXMemberCallExpr(CXXMemberCallExpr const* expr) { - auto const dc = loplugin::DeclCheck(expr->getMethodDecl()).Function("copy"); - if (!dc) + auto const dc1 = loplugin::DeclCheck(expr->getMethodDecl()); + if (auto const 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 (!(dc.Class("OString").Namespace("rtl").GlobalNamespace() - || dc.Class("OUString").Namespace("rtl").GlobalNamespace())) + if (auto const 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; } - report(DiagnosticsEngine::Warning, "rather than copy, pass with a view using subView()", - expr->getExprLoc()) - << expr->getSourceRange(); + if (auto const 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 (auto const dc2 = dc1.Function("makeStringAndClear")) + { + if (dc2.Class("OStringBuffer").Namespace("rtl").GlobalNamespace() + || dc2.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()) + { + auto const 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 (auto const 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)) + { + return true; + } + /** 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()) + { + auto const dc = loplugin::DeclCheck(expr->getMethodDecl()); + if (dc.Function("append") || dc.Function("indexOf") || dc.Function("lastIndexOf")) + { + handleSubExprThatCouldBeView(expr->getArg(0)); + } + else if (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()) + { + auto const 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()); + } + } + return true; +} + +/** 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)) + { + return true; + } + if (!loplugin::TypeCheck(expr->getType()) + .Class("OUStringBuffer") + .Namespace("rtl") + .GlobalNamespace() + && !loplugin::TypeCheck(expr->getType()) + .Class("OStringBuffer") + .Namespace("rtl") + .GlobalNamespace()) + { + return true; + } + if (!compiler.getLangOpts().CPlusPlus17 && expr->isElidable()) // external C++03 code + { + return true; + } + if (expr->getNumArgs() > 0) + handleSubExprThatCouldBeView(expr->getArg(0)); + return true; } loplugin::Plugin::Registration<StringView> stringview("stringview"); |