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
18 // Find uses of OUString in conditional expressions that could be rewritten as OUStringLiteral, as
21 // s += (b ? OUString("xy") : OUString(z");
25 // Like Expr::IgnoreImplicit, but for an ImplicitCastExpr skips to getSubExprAsWritten (so skips a
26 // CXXConstructExpr where Expr::IgnoreImplicit would stop):
27 Expr
const* ignoreImplicit(Expr
const* expr
)
31 if (auto const e1
= dyn_cast
<ImplicitCastExpr
>(e
))
33 e
= e1
->getSubExprAsWritten();
35 #if CLANG_VERSION >= 80000
36 else if (auto const e2
= dyn_cast
<FullExpr
>(e
))
41 else if (auto const e3
= dyn_cast
<MaterializeTemporaryExpr
>(e
))
43 e
= e3
->GetTemporaryExpr();
45 else if (auto const e4
= dyn_cast
<CXXBindTemporaryExpr
>(e
))
56 class ConditionalString final
: public loplugin::FilteringPlugin
<ConditionalString
>
59 explicit ConditionalString(loplugin::InstantiationData
const& data
)
60 : FilteringPlugin(data
)
64 bool VisitCallExpr(CallExpr
const* expr
)
66 if (ignoreLocation(expr
))
70 auto const fn
= expr
->getDirectCallee();
75 //TODO: Instead of a hardcoded list of functions, check that `fn` has overloads taking
76 // OUString and OUStringLiteral, respectively (and operator + is even more complicated than
77 // that, going via ToStringHelper<OUStringLiteral> specialization; the getNumArgs checks for
78 // the various functions are meant to guard against the unlikely case that the affected
79 // parameters get defaulted in the future; overloaded operators cannot generally have
80 // defaulted parameters):
81 loplugin::DeclCheck
const dc(fn
);
82 if (dc
.Operator(OO_Equal
).Class("OUString").Namespace("rtl").GlobalNamespace())
84 assert(fn
->getNumParams() == 1);
85 if (isa
<CXXOperatorCallExpr
>(expr
))
87 assert(expr
->getNumArgs() == 2);
88 check(expr
->getArg(1));
92 assert(expr
->getNumArgs() == 1);
93 check(expr
->getArg(0));
97 if (dc
.Operator(OO_PlusEqual
).Class("OUString").Namespace("rtl").GlobalNamespace())
99 assert(fn
->getNumParams() == 1);
100 if (isa
<CXXOperatorCallExpr
>(expr
))
102 assert(expr
->getNumArgs() == 2);
103 check(expr
->getArg(1));
107 assert(expr
->getNumArgs() == 1);
108 check(expr
->getArg(0));
112 if (dc
.Function("reverseCompareTo").Class("OUString").Namespace("rtl").GlobalNamespace()
113 && fn
->getNumParams() == 1)
115 if (expr
->getNumArgs() >= 1)
117 check(expr
->getArg(0));
121 if (dc
.Function("equalsIgnoreAsciiCase")
125 && fn
->getNumParams() == 1)
127 if (expr
->getNumArgs() >= 1)
129 check(expr
->getArg(0));
133 if (dc
.Function("match").Class("OUString").Namespace("rtl").GlobalNamespace()
134 && fn
->getNumParams() == 2)
136 if (expr
->getNumArgs() >= 1)
138 check(expr
->getArg(0));
142 if (dc
.Function("matchIgnoreAsciiCase").Class("OUString").Namespace("rtl").GlobalNamespace()
143 && fn
->getNumParams() == 2)
145 if (expr
->getNumArgs() >= 1)
147 check(expr
->getArg(0));
151 if (dc
.Function("startsWith").Class("OUString").Namespace("rtl").GlobalNamespace()
152 && fn
->getNumParams() == 2)
154 if (expr
->getNumArgs() >= 1)
156 check(expr
->getArg(0));
160 if (dc
.Function("startsWithIgnoreAsciiCase")
164 && fn
->getNumParams() == 2)
166 if (expr
->getNumArgs() >= 1)
168 check(expr
->getArg(0));
172 if (dc
.Function("endsWith").Class("OUString").Namespace("rtl").GlobalNamespace()
173 && fn
->getNumParams() == 2)
175 if (expr
->getNumArgs() >= 1)
177 check(expr
->getArg(0));
181 if (dc
.Function("endsWithIgnoreAsciiCase")
185 && fn
->getNumParams() == 2)
187 if (expr
->getNumArgs() >= 1)
189 check(expr
->getArg(0));
193 if (dc
.Operator(OO_EqualEqual
)
195 .GlobalNamespace()) //TODO: more precicse check
197 assert(fn
->getNumParams() == 2);
198 assert(expr
->getNumArgs() == 2);
199 check(expr
->getArg(0));
200 check(expr
->getArg(1));
203 if (dc
.Operator(OO_ExclaimEqual
)
205 .GlobalNamespace()) //TODO: more precicse check
207 assert(fn
->getNumParams() == 2);
208 assert(expr
->getNumArgs() == 2);
209 check(expr
->getArg(0));
210 check(expr
->getArg(1));
213 if (dc
.Operator(OO_Less
).Namespace("rtl").GlobalNamespace()) //TODO: more precicse check
215 assert(fn
->getNumParams() == 2);
216 assert(expr
->getNumArgs() == 2);
217 check(expr
->getArg(0));
218 check(expr
->getArg(1));
221 if (dc
.Operator(OO_LessEqual
)
223 .GlobalNamespace()) //TODO: more precicse check
225 assert(fn
->getNumParams() == 2);
226 assert(expr
->getNumArgs() == 2);
227 check(expr
->getArg(0));
228 check(expr
->getArg(1));
231 if (dc
.Operator(OO_Greater
).Namespace("rtl").GlobalNamespace()) //TODO: more precicse check
233 assert(fn
->getNumParams() == 2);
234 assert(expr
->getNumArgs() == 2);
235 check(expr
->getArg(0));
236 check(expr
->getArg(1));
239 if (dc
.Operator(OO_GreaterEqual
)
241 .GlobalNamespace()) //TODO: more precicse check
243 assert(fn
->getNumParams() == 2);
244 assert(expr
->getNumArgs() == 2);
245 check(expr
->getArg(0));
246 check(expr
->getArg(1));
249 if (dc
.Function("indexOf").Class("OUString").Namespace("rtl").GlobalNamespace()
250 && fn
->getNumParams() == 2)
252 if (expr
->getNumArgs() >= 1)
254 check(expr
->getArg(0));
258 if (dc
.Function("lastIndexOf").Class("OUString").Namespace("rtl").GlobalNamespace()
259 && fn
->getNumParams() == 1)
261 if (expr
->getNumArgs() >= 1)
263 check(expr
->getArg(0));
267 if (dc
.Function("replaceFirst").Class("OUString").Namespace("rtl").GlobalNamespace()
268 && fn
->getNumParams() == 3)
270 if (expr
->getNumArgs() >= 1)
272 check(expr
->getArg(0));
274 if (expr
->getNumArgs() >= 2)
276 check(expr
->getArg(1));
280 if (dc
.Function("replaceAll").Class("OUString").Namespace("rtl").GlobalNamespace()
281 && fn
->getNumParams() == 2)
283 if (expr
->getNumArgs() >= 1)
285 check(expr
->getArg(0));
287 if (expr
->getNumArgs() >= 2)
289 check(expr
->getArg(1));
293 if (dc
.Operator(OO_Plus
).Namespace("rtl").GlobalNamespace()
294 && fn
->getNumParams() == 2) //TODO: more precicse check
296 assert(expr
->getNumArgs() == 2);
297 check(expr
->getArg(0));
298 check(expr
->getArg(1));
301 if (dc
.Operator(OO_Equal
).Class("OUStringBuffer").Namespace("rtl").GlobalNamespace())
303 assert(fn
->getNumParams() == 1);
304 if (isa
<CXXOperatorCallExpr
>(expr
))
306 assert(expr
->getNumArgs() == 2);
307 check(expr
->getArg(1));
311 assert(expr
->getNumArgs() == 1);
312 check(expr
->getArg(0));
316 if (dc
.Function("append").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
317 && fn
->getNumParams() == 1)
319 if (expr
->getNumArgs() >= 1)
321 check(expr
->getArg(0));
325 if (dc
.Function("insert").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
326 && fn
->getNumParams() == 2)
328 if (expr
->getNumArgs() >= 2)
330 check(expr
->getArg(1));
334 if (dc
.Function("indexOf").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
335 && fn
->getNumParams() == 2)
337 if (expr
->getNumArgs() >= 1)
339 check(expr
->getArg(0));
343 if (dc
.Function("lastIndexOf").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
344 && fn
->getNumParams() == 1)
346 if (expr
->getNumArgs() >= 1)
348 check(expr
->getArg(0));
355 bool preRun() override
{ return compiler
.getLangOpts().CPlusPlus
; }
361 OUStringLiteralOrVoid
,
369 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
373 Kind
getKind(Expr
const* expr
)
375 auto const tc
= loplugin::TypeCheck(ignoreImplicit(expr
)->getType());
376 if (tc
.Struct("OUStringLiteral").Namespace("rtl").GlobalNamespace() || tc
.Void())
378 return Kind::OUStringLiteralOrVoid
;
380 if (loplugin::TypeCheck(expr
->getType())
385 // Check for both explicit
394 auto e
= expr
->IgnoreParens();
395 if (auto const e1
= dyn_cast
<CXXFunctionalCastExpr
>(e
))
397 e
= e1
->getSubExpr();
400 = dyn_cast
<CXXConstructExpr
>(compat::IgnoreImplicit(e
)->IgnoreParens()))
402 if (e1
->getNumArgs() != 0 //TODO
403 && isa
<clang::StringLiteral
>(e1
->getArg(0)->IgnoreParenImpCasts()))
405 return Kind::OUStringFromLiteral
;
412 void check(Expr
const* expr
)
414 //TODO: skip `,`; handle ?: chains
415 auto const cond
= dyn_cast
<ConditionalOperator
>(expr
->IgnoreParenImpCasts());
420 auto const k1
= getKind(cond
->getTrueExpr());
421 if (k1
== Kind::Other
)
425 auto const k2
= getKind(cond
->getFalseExpr());
426 if (k2
== Kind::Other
427 || (k1
== Kind::OUStringLiteralOrVoid
&& k2
== Kind::OUStringLiteralOrVoid
))
431 if (k1
== Kind::OUStringFromLiteral
&& k2
== Kind::OUStringFromLiteral
)
433 report(DiagnosticsEngine::Warning
,
434 ("replace both 2nd and 3rd operands of conditional expression with"
435 " `rtl::OUStringLiteral`"),
437 << cond
->getSourceRange();
441 assert((k1
== Kind::OUStringFromLiteral
&& k2
== Kind::OUStringLiteralOrVoid
)
442 || (k1
== Kind::OUStringLiteralOrVoid
&& k2
== Kind::OUStringFromLiteral
));
443 auto const second
= k1
== Kind::OUStringFromLiteral
;
445 = (second
? cond
->getTrueExpr() : cond
->getFalseExpr())->IgnoreParenImpCasts();
446 report(DiagnosticsEngine::Warning
,
447 ("replace %select{2nd|3rd}0 operand of conditional expression with"
448 " `rtl::OUStringLiteral`"),
450 << (second
? 0 : 1) << sub
->getSourceRange();
451 report(DiagnosticsEngine::Note
, "conditional expression is here", cond
->getExprLoc())
452 << cond
->getSourceRange();
457 loplugin::Plugin::Registration
<ConditionalString
> conditionalstring("conditionalstring");
462 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */