1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
10 #ifndef LO_CLANG_SHARED_PLUGINS
17 // Find uses of OUString in conditional expressions that could be rewritten as std::u16string_view,
20 // s += (b ? OUString("xy") : OUString(z");
24 // Like Expr::IgnoreImplicit, but for an ImplicitCastExpr skips to getSubExprAsWritten (so skips a
25 // CXXConstructExpr where Expr::IgnoreImplicit would stop):
26 Expr
const* ignoreImplicit(Expr
const* expr
)
30 if (auto const e1
= dyn_cast
<ImplicitCastExpr
>(e
))
32 e
= e1
->getSubExprAsWritten();
34 else if (auto const e2
= dyn_cast
<FullExpr
>(e
))
38 else if (auto const e3
= dyn_cast
<MaterializeTemporaryExpr
>(e
))
42 else if (auto const e4
= dyn_cast
<CXXBindTemporaryExpr
>(e
))
53 class ConditionalString final
: public loplugin::FilteringPlugin
<ConditionalString
>
56 explicit ConditionalString(loplugin::InstantiationData
const& data
)
57 : FilteringPlugin(data
)
61 bool VisitCallExpr(CallExpr
const* expr
)
63 if (ignoreLocation(expr
))
67 auto const fn
= expr
->getDirectCallee();
72 //TODO: Instead of a hardcoded list of functions, check that `fn` has overloads taking
73 // OUString and std::u16string_view, respectively (and operator + is even more complicated
74 // than that, going via ToStringHelper<std::u16string_view> specialization; the getNumArgs
75 // checks for the various functions are meant to guard against the unlikely case that the
76 // affected parameters get defaulted in the future; overloaded operators cannot generally
77 // have defaulted parameters):
78 loplugin::DeclCheck
const dc(fn
);
79 if (dc
.Operator(OO_Equal
).Class("OUString").Namespace("rtl").GlobalNamespace())
81 assert(fn
->getNumParams() == 1);
82 if (isa
<CXXOperatorCallExpr
>(expr
))
84 assert(expr
->getNumArgs() == 2);
85 check(expr
->getArg(1));
89 assert(expr
->getNumArgs() == 1);
90 check(expr
->getArg(0));
94 if (dc
.Operator(OO_PlusEqual
).Class("OUString").Namespace("rtl").GlobalNamespace())
96 assert(fn
->getNumParams() == 1);
97 if (isa
<CXXOperatorCallExpr
>(expr
))
99 assert(expr
->getNumArgs() == 2);
100 check(expr
->getArg(1));
104 assert(expr
->getNumArgs() == 1);
105 check(expr
->getArg(0));
109 if (dc
.Function("reverseCompareTo").Class("OUString").Namespace("rtl").GlobalNamespace()
110 && fn
->getNumParams() == 1)
112 if (expr
->getNumArgs() >= 1)
114 check(expr
->getArg(0));
118 if (dc
.Function("equalsIgnoreAsciiCase")
122 && fn
->getNumParams() == 1)
124 if (expr
->getNumArgs() >= 1)
126 check(expr
->getArg(0));
130 if (dc
.Function("match").Class("OUString").Namespace("rtl").GlobalNamespace()
131 && fn
->getNumParams() == 2)
133 if (expr
->getNumArgs() >= 1)
135 check(expr
->getArg(0));
139 if (dc
.Function("matchIgnoreAsciiCase").Class("OUString").Namespace("rtl").GlobalNamespace()
140 && fn
->getNumParams() == 2)
142 if (expr
->getNumArgs() >= 1)
144 check(expr
->getArg(0));
148 if (dc
.Function("startsWith").Class("OUString").Namespace("rtl").GlobalNamespace()
149 && fn
->getNumParams() == 2)
151 if (expr
->getNumArgs() >= 1)
153 check(expr
->getArg(0));
157 if (dc
.Function("startsWithIgnoreAsciiCase")
161 && fn
->getNumParams() == 2)
163 if (expr
->getNumArgs() >= 1)
165 check(expr
->getArg(0));
169 if (dc
.Function("endsWith").Class("OUString").Namespace("rtl").GlobalNamespace()
170 && fn
->getNumParams() == 2)
172 if (expr
->getNumArgs() >= 1)
174 check(expr
->getArg(0));
178 if (dc
.Function("endsWithIgnoreAsciiCase")
182 && fn
->getNumParams() == 2)
184 if (expr
->getNumArgs() >= 1)
186 check(expr
->getArg(0));
190 if (dc
.Operator(OO_EqualEqual
)
192 .GlobalNamespace()) //TODO: more precicse check
194 assert(fn
->getNumParams() == 2);
195 assert(expr
->getNumArgs() == 2);
196 check(expr
->getArg(0));
197 check(expr
->getArg(1));
200 if (dc
.Operator(OO_ExclaimEqual
)
202 .GlobalNamespace()) //TODO: more precicse check
204 assert(fn
->getNumParams() == 2);
205 assert(expr
->getNumArgs() == 2);
206 check(expr
->getArg(0));
207 check(expr
->getArg(1));
210 if (dc
.Operator(OO_Less
).Namespace("rtl").GlobalNamespace()) //TODO: more precicse check
212 assert(fn
->getNumParams() == 2);
213 assert(expr
->getNumArgs() == 2);
214 check(expr
->getArg(0));
215 check(expr
->getArg(1));
218 if (dc
.Operator(OO_LessEqual
)
220 .GlobalNamespace()) //TODO: more precicse check
222 assert(fn
->getNumParams() == 2);
223 assert(expr
->getNumArgs() == 2);
224 check(expr
->getArg(0));
225 check(expr
->getArg(1));
228 if (dc
.Operator(OO_Greater
).Namespace("rtl").GlobalNamespace()) //TODO: more precicse check
230 assert(fn
->getNumParams() == 2);
231 assert(expr
->getNumArgs() == 2);
232 check(expr
->getArg(0));
233 check(expr
->getArg(1));
236 if (dc
.Operator(OO_GreaterEqual
)
238 .GlobalNamespace()) //TODO: more precicse check
240 assert(fn
->getNumParams() == 2);
241 assert(expr
->getNumArgs() == 2);
242 check(expr
->getArg(0));
243 check(expr
->getArg(1));
246 if (dc
.Function("indexOf").Class("OUString").Namespace("rtl").GlobalNamespace()
247 && fn
->getNumParams() == 2)
249 if (expr
->getNumArgs() >= 1)
251 check(expr
->getArg(0));
255 if (dc
.Function("lastIndexOf").Class("OUString").Namespace("rtl").GlobalNamespace()
256 && fn
->getNumParams() == 1)
258 if (expr
->getNumArgs() >= 1)
260 check(expr
->getArg(0));
264 if (dc
.Function("replaceFirst").Class("OUString").Namespace("rtl").GlobalNamespace()
265 && fn
->getNumParams() == 3)
267 if (expr
->getNumArgs() >= 1)
269 check(expr
->getArg(0));
271 if (expr
->getNumArgs() >= 2)
273 check(expr
->getArg(1));
277 if (dc
.Function("replaceAll").Class("OUString").Namespace("rtl").GlobalNamespace()
278 && fn
->getNumParams() == 2)
280 if (expr
->getNumArgs() >= 1)
282 check(expr
->getArg(0));
284 if (expr
->getNumArgs() >= 2)
286 check(expr
->getArg(1));
290 if (dc
.Operator(OO_Plus
).Namespace("rtl").GlobalNamespace()
291 && fn
->getNumParams() == 2) //TODO: more precicse check
293 assert(expr
->getNumArgs() == 2);
294 check(expr
->getArg(0));
295 check(expr
->getArg(1));
298 if (dc
.Operator(OO_Equal
).Class("OUStringBuffer").Namespace("rtl").GlobalNamespace())
300 assert(fn
->getNumParams() == 1);
301 if (isa
<CXXOperatorCallExpr
>(expr
))
303 assert(expr
->getNumArgs() == 2);
304 check(expr
->getArg(1));
308 assert(expr
->getNumArgs() == 1);
309 check(expr
->getArg(0));
313 if (dc
.Function("append").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
314 && fn
->getNumParams() == 1)
316 if (expr
->getNumArgs() >= 1)
318 check(expr
->getArg(0));
322 if (dc
.Function("insert").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
323 && fn
->getNumParams() == 2)
325 if (expr
->getNumArgs() >= 2)
327 check(expr
->getArg(1));
331 if (dc
.Function("indexOf").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
332 && fn
->getNumParams() == 2)
334 if (expr
->getNumArgs() >= 1)
336 check(expr
->getArg(0));
340 if (dc
.Function("lastIndexOf").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
341 && fn
->getNumParams() == 1)
343 if (expr
->getNumArgs() >= 1)
345 check(expr
->getArg(0));
352 bool preRun() override
{ return compiler
.getLangOpts().CPlusPlus
; }
366 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
370 Kind
getKind(Expr
const* expr
)
372 auto const tc
= loplugin::TypeCheck(ignoreImplicit(expr
)->getType());
373 if (tc
.ClassOrStruct("basic_string_view").StdNamespace() //TODO: check explicitly for
374 // std::basic_string_view<char16_t>
377 return Kind::StringViewOrVoid
;
379 if (loplugin::TypeCheck(expr
->getType())
384 // Check for both explicit
393 auto e
= expr
->IgnoreParens();
394 if (auto const e1
= dyn_cast
<CXXFunctionalCastExpr
>(e
))
396 e
= e1
->getSubExpr();
398 if (auto const e1
= dyn_cast
<CXXConstructExpr
>(e
->IgnoreImplicit()->IgnoreParens()))
400 if (e1
->getNumArgs() != 0 //TODO
401 && isa
<clang::StringLiteral
>(e1
->getArg(0)->IgnoreParenImpCasts()))
403 return Kind::OUStringFromLiteral
;
410 void check(Expr
const* expr
)
412 //TODO: skip `,`; handle ?: chains
413 auto const cond
= dyn_cast
<ConditionalOperator
>(expr
->IgnoreParenImpCasts());
418 auto const k1
= getKind(cond
->getTrueExpr());
419 if (k1
== Kind::Other
)
423 auto const k2
= getKind(cond
->getFalseExpr());
424 if (k2
== Kind::Other
|| (k1
== Kind::StringViewOrVoid
&& k2
== Kind::StringViewOrVoid
))
428 if (k1
== Kind::OUStringFromLiteral
&& k2
== Kind::OUStringFromLiteral
)
430 report(DiagnosticsEngine::Warning
,
431 ("replace both 2nd and 3rd operands of conditional expression with"
432 " `std::u16string_view`"),
434 << cond
->getSourceRange();
438 assert((k1
== Kind::OUStringFromLiteral
&& k2
== Kind::StringViewOrVoid
)
439 || (k1
== Kind::StringViewOrVoid
&& k2
== Kind::OUStringFromLiteral
));
440 auto const second
= k1
== Kind::OUStringFromLiteral
;
442 = (second
? cond
->getTrueExpr() : cond
->getFalseExpr())->IgnoreParenImpCasts();
443 report(DiagnosticsEngine::Warning
,
444 ("replace %select{2nd|3rd}0 operand of conditional expression with"
445 " `std::u16string_view`"),
447 << (second
? 0 : 1) << sub
->getSourceRange();
448 report(DiagnosticsEngine::Note
, "conditional expression is here", cond
->getExprLoc())
449 << cond
->getSourceRange();
454 loplugin::Plugin::Registration
<ConditionalString
> conditionalstring("conditionalstring");
459 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */