1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 #ifndef LO_CLANG_SHARED_PLUGINS
14 #include <unordered_map>
15 #include <unordered_set>
19 #include "clang/AST/CXXInheritance.h"
20 #include "clang/AST/StmtVisitor.h"
23 Look for places where we are making a substring copy of an OUString, and then passing it to a
24 function that takes a u16string_view, in which case it is more efficient to pass a view
25 of the OUString, rather than making a copy.
27 TODO currently does not check if there is some other visible overload of the callee, that can take
29 TODO handle OUStringBuffer/OStringBuffer similarly
34 class StringView
: public loplugin::FilteringPlugin
<StringView
>
37 explicit StringView(loplugin::InstantiationData
const& data
)
38 : FilteringPlugin(data
)
42 bool preRun() override
44 auto const fn
= handler
.getMainFileName();
46 loplugin::isSamePathname(fn
, SRCDIR
"/sal/qa/OStringBuffer/rtl_OStringBuffer.cxx")
47 || loplugin::isSamePathname(fn
, SRCDIR
"/sal/qa/rtl/strings/test_ostring_concat.cxx")
48 || loplugin::isSamePathname(fn
, SRCDIR
"/sal/qa/rtl/strings/test_oustring_concat.cxx"));
51 virtual void run() override
55 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
58 bool VisitFunctionDecl(FunctionDecl
const*);
59 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr
const*);
60 bool VisitImplicitCastExpr(ImplicitCastExpr
const*);
61 bool VisitCXXMemberCallExpr(CXXMemberCallExpr
const*);
62 bool VisitCXXConstructExpr(CXXConstructExpr
const*);
65 void handleSubExprThatCouldBeView(Expr
const* expr
);
66 void handleCXXConstructExpr(CXXConstructExpr
const* expr
);
67 void handleCXXMemberCallExpr(CXXMemberCallExpr
const* expr
);
70 bool StringView::VisitCXXOperatorCallExpr(CXXOperatorCallExpr
const* cxxOperatorCallExpr
)
72 if (ignoreLocation(cxxOperatorCallExpr
))
75 auto op
= cxxOperatorCallExpr
->getOperator();
76 if (op
== OO_Plus
&& cxxOperatorCallExpr
->getNumArgs() == 2)
78 handleSubExprThatCouldBeView(cxxOperatorCallExpr
->getArg(0));
79 handleSubExprThatCouldBeView(cxxOperatorCallExpr
->getArg(1));
81 if (compat::isComparisonOp(cxxOperatorCallExpr
))
83 handleSubExprThatCouldBeView(cxxOperatorCallExpr
->getArg(0));
84 handleSubExprThatCouldBeView(cxxOperatorCallExpr
->getArg(1));
86 else if (op
== OO_PlusEqual
)
87 handleSubExprThatCouldBeView(cxxOperatorCallExpr
->getArg(1));
88 else if (op
== OO_Subscript
)
89 handleSubExprThatCouldBeView(cxxOperatorCallExpr
->getArg(0));
90 else if (op
== OO_Equal
)
92 if (loplugin::TypeCheck(cxxOperatorCallExpr
->getType())
93 .Class("OUStringBuffer")
96 || loplugin::TypeCheck(cxxOperatorCallExpr
->getType())
97 .Class("OStringBuffer")
101 handleSubExprThatCouldBeView(cxxOperatorCallExpr
->getArg(1));
107 bool StringView::VisitFunctionDecl(FunctionDecl
const* functionDecl
)
109 if (ignoreLocation(functionDecl
))
112 // if (functionDecl->getIdentifier() && functionDecl->getName() == "f1")
113 // functionDecl->dump();
117 bool StringView::VisitImplicitCastExpr(ImplicitCastExpr
const* expr
)
119 if (ignoreLocation(expr
))
123 if (!loplugin::TypeCheck(expr
->getType()).ClassOrStruct("basic_string_view").StdNamespace())
127 handleSubExprThatCouldBeView(expr
->getSubExprAsWritten());
131 void StringView::handleSubExprThatCouldBeView(Expr
const* subExpr
)
133 auto const e0
= compat::IgnoreImplicit(subExpr
);
134 auto const e
= e0
->IgnoreParens();
135 auto const tc
= loplugin::TypeCheck(e
->getType());
136 if (!(tc
.Class("OString").Namespace("rtl").GlobalNamespace()
137 || tc
.Class("OUString").Namespace("rtl").GlobalNamespace()))
141 if (auto const e1
= dyn_cast
<CXXConstructExpr
>(e
))
145 handleCXXConstructExpr(e1
);
148 else if (auto const e2
= dyn_cast
<CXXFunctionalCastExpr
>(e
))
150 auto e3
= e2
->getSubExpr();
151 if (auto const e4
= dyn_cast
<CXXBindTemporaryExpr
>(e3
))
153 e3
= e4
->getSubExpr();
155 if (auto const e4
= dyn_cast
<CXXConstructExpr
>(e3
))
157 handleCXXConstructExpr(e4
);
160 else if (auto const e3
= dyn_cast
<CXXMemberCallExpr
>(e
))
162 handleCXXMemberCallExpr(e3
);
166 void StringView::handleCXXConstructExpr(CXXConstructExpr
const* expr
)
176 auto const d
= expr
->getConstructor();
177 switch (d
->getNumParams())
183 auto const t
= d
->getParamDecl(0)->getType();
184 if (t
->isAnyCharacterType())
186 argType
= expr
->getArg(0)->IgnoreImplicit()->getType();
190 loplugin::TypeCheck
tc(t
);
191 if (tc
.LvalueReference()
193 .Class("OStringLiteral")
196 || tc
.LvalueReference()
198 .Class("OUStringLiteral")
201 || tc
.RvalueReference().Struct("OStringNumber").Namespace("rtl").GlobalNamespace()
202 || tc
.RvalueReference().Struct("OUStringNumber").Namespace("rtl").GlobalNamespace()
203 || tc
.ClassOrStruct("basic_string_view").StdNamespace())
205 argType
= expr
->getArg(0)->IgnoreImplicit()->getType();
208 if (tc
.RvalueReference().Struct("OStringConcat").Namespace("rtl").GlobalNamespace()
209 || tc
.RvalueReference().Struct("OUStringConcat").Namespace("rtl").GlobalNamespace())
211 argType
= expr
->getArg(0)->IgnoreImplicit()->getType();
212 extra
= ViaConcatenation
;
219 auto const t0
= d
->getParamDecl(0)->getType();
220 if (t0
->isPointerType() && t0
->getPointeeType()->isAnyCharacterType())
222 auto const t
= d
->getParamDecl(1)->getType();
223 if (t
->isIntegralType(compiler
.getASTContext())
224 && !(t
->isBooleanType() || t
->isAnyCharacterType()))
226 auto const arg
= expr
->getArg(1);
227 if (!arg
->isValueDependent())
230 = compat::getIntegerConstantExpr(arg
, compiler
.getASTContext()))
232 if (val
->getExtValue() == 1)
238 argType
= expr
->getArg(0)->IgnoreImplicit()->getType();
242 if (loplugin::TypeCheck(d
->getParamDecl(1)->getType())
244 .Namespace("libreoffice_internal")
248 argType
= expr
->getArg(0)->IgnoreImplicit()->getType();
256 report(DiagnosticsEngine::Warning
,
257 "instead of an %0%select{| constructed from a %2}1, pass a"
258 " '%select{std::string_view|std::u16string_view}3'"
259 "%select{| (or an '%select{rtl::OStringChar|rtl::OUStringChar}3')|"
260 " via '%select{rtl::OStringConcatenation|rtl::OUStringConcatenation}3'}4",
262 << expr
->getType() << (argType
.isNull() ? 0 : 1) << argType
263 << (loplugin::TypeCheck(expr
->getType()).Class("OString").Namespace("rtl").GlobalNamespace()
266 << extra
<< expr
->getSourceRange();
269 void StringView::handleCXXMemberCallExpr(CXXMemberCallExpr
const* expr
)
271 auto const dc
= loplugin::DeclCheck(expr
->getMethodDecl()).Function("copy");
276 if (!(dc
.Class("OString").Namespace("rtl").GlobalNamespace()
277 || dc
.Class("OUString").Namespace("rtl").GlobalNamespace()))
281 report(DiagnosticsEngine::Warning
, "rather than copy, pass with a view using subView()",
283 << expr
->getSourceRange();
286 /** check for calls to O[U]StringBuffer::append that could be passed as a
287 std::u16string_view */
288 bool StringView::VisitCXXMemberCallExpr(CXXMemberCallExpr
const* expr
)
290 if (ignoreLocation(expr
))
294 if (!loplugin::TypeCheck(expr
->getType())
295 .Class("OUStringBuffer")
298 && !loplugin::TypeCheck(expr
->getType())
299 .Class("OStringBuffer")
305 auto const dc
= loplugin::DeclCheck(expr
->getMethodDecl());
306 if (dc
.Function("append") || dc
.Function("indexOf") || dc
.Function("lastIndexOf"))
308 handleSubExprThatCouldBeView(expr
->getArg(0));
310 else if (dc
.Function("insert"))
312 handleSubExprThatCouldBeView(expr
->getArg(1));
317 /** check for calls to O[U]StringBuffer constructor that could be passed as a
318 std::u16string_view */
319 bool StringView::VisitCXXConstructExpr(CXXConstructExpr
const* expr
)
321 if (ignoreLocation(expr
))
325 if (!loplugin::TypeCheck(expr
->getType())
326 .Class("OUStringBuffer")
329 && !loplugin::TypeCheck(expr
->getType())
330 .Class("OStringBuffer")
336 if (expr
->getNumArgs() > 0)
337 handleSubExprThatCouldBeView(expr
->getArg(0));
341 loplugin::Plugin::Registration
<StringView
> stringview("stringview");
344 #endif // LO_CLANG_SHARED_PLUGINS
346 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */