summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/stringview.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/stringview.cxx')
-rw-r--r--compilerplugins/clang/stringview.cxx268
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");