Remove exec bits from docx
[LibreOffice.git] / compilerplugins / clang / conditionalstring.cxx
blobcf858601ea0a76da65c888e9e42c5d56ea06ffff
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
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/.
8 */
10 #ifndef LO_CLANG_SHARED_PLUGINS
12 #include <cassert>
14 #include "check.hxx"
15 #include "plugin.hxx"
17 // Find uses of OUString in conditional expressions that could be rewritten as std::u16string_view,
18 // as in
20 // s += (b ? OUString("xy") : OUString(z");
22 namespace
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)
28 for (auto e = expr;;)
30 if (auto const e1 = dyn_cast<ImplicitCastExpr>(e))
32 e = e1->getSubExprAsWritten();
34 else if (auto const e2 = dyn_cast<FullExpr>(e))
36 e = e2->getSubExpr();
38 else if (auto const e3 = dyn_cast<MaterializeTemporaryExpr>(e))
40 e = e3->getSubExpr();
42 else if (auto const e4 = dyn_cast<CXXBindTemporaryExpr>(e))
44 e = e4->getSubExpr();
46 else
48 return e;
53 class ConditionalString final : public loplugin::FilteringPlugin<ConditionalString>
55 public:
56 explicit ConditionalString(loplugin::InstantiationData const& data)
57 : FilteringPlugin(data)
61 bool VisitCallExpr(CallExpr const* expr)
63 if (ignoreLocation(expr))
65 return true;
67 auto const fn = expr->getDirectCallee();
68 if (fn == nullptr)
70 return true;
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));
87 else
89 assert(expr->getNumArgs() == 1);
90 check(expr->getArg(0));
92 return true;
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));
102 else
104 assert(expr->getNumArgs() == 1);
105 check(expr->getArg(0));
107 return true;
109 if (dc.Function("reverseCompareTo").Class("OUString").Namespace("rtl").GlobalNamespace()
110 && fn->getNumParams() == 1)
112 if (expr->getNumArgs() >= 1)
114 check(expr->getArg(0));
116 return true;
118 if (dc.Function("equalsIgnoreAsciiCase")
119 .Class("OUString")
120 .Namespace("rtl")
121 .GlobalNamespace()
122 && fn->getNumParams() == 1)
124 if (expr->getNumArgs() >= 1)
126 check(expr->getArg(0));
128 return true;
130 if (dc.Function("match").Class("OUString").Namespace("rtl").GlobalNamespace()
131 && fn->getNumParams() == 2)
133 if (expr->getNumArgs() >= 1)
135 check(expr->getArg(0));
137 return true;
139 if (dc.Function("matchIgnoreAsciiCase").Class("OUString").Namespace("rtl").GlobalNamespace()
140 && fn->getNumParams() == 2)
142 if (expr->getNumArgs() >= 1)
144 check(expr->getArg(0));
146 return true;
148 if (dc.Function("startsWith").Class("OUString").Namespace("rtl").GlobalNamespace()
149 && fn->getNumParams() == 2)
151 if (expr->getNumArgs() >= 1)
153 check(expr->getArg(0));
155 return true;
157 if (dc.Function("startsWithIgnoreAsciiCase")
158 .Class("OUString")
159 .Namespace("rtl")
160 .GlobalNamespace()
161 && fn->getNumParams() == 2)
163 if (expr->getNumArgs() >= 1)
165 check(expr->getArg(0));
167 return true;
169 if (dc.Function("endsWith").Class("OUString").Namespace("rtl").GlobalNamespace()
170 && fn->getNumParams() == 2)
172 if (expr->getNumArgs() >= 1)
174 check(expr->getArg(0));
176 return true;
178 if (dc.Function("endsWithIgnoreAsciiCase")
179 .Class("OUString")
180 .Namespace("rtl")
181 .GlobalNamespace()
182 && fn->getNumParams() == 2)
184 if (expr->getNumArgs() >= 1)
186 check(expr->getArg(0));
188 return true;
190 if (dc.Operator(OO_EqualEqual)
191 .Namespace("rtl")
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));
198 return true;
200 if (dc.Operator(OO_ExclaimEqual)
201 .Namespace("rtl")
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));
208 return true;
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));
216 return true;
218 if (dc.Operator(OO_LessEqual)
219 .Namespace("rtl")
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));
226 return true;
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));
234 return true;
236 if (dc.Operator(OO_GreaterEqual)
237 .Namespace("rtl")
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));
244 return true;
246 if (dc.Function("indexOf").Class("OUString").Namespace("rtl").GlobalNamespace()
247 && fn->getNumParams() == 2)
249 if (expr->getNumArgs() >= 1)
251 check(expr->getArg(0));
253 return true;
255 if (dc.Function("lastIndexOf").Class("OUString").Namespace("rtl").GlobalNamespace()
256 && fn->getNumParams() == 1)
258 if (expr->getNumArgs() >= 1)
260 check(expr->getArg(0));
262 return true;
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));
275 return true;
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));
288 return true;
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));
296 return true;
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));
306 else
308 assert(expr->getNumArgs() == 1);
309 check(expr->getArg(0));
311 return true;
313 if (dc.Function("append").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
314 && fn->getNumParams() == 1)
316 if (expr->getNumArgs() >= 1)
318 check(expr->getArg(0));
320 return true;
322 if (dc.Function("insert").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
323 && fn->getNumParams() == 2)
325 if (expr->getNumArgs() >= 2)
327 check(expr->getArg(1));
329 return true;
331 if (dc.Function("indexOf").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
332 && fn->getNumParams() == 2)
334 if (expr->getNumArgs() >= 1)
336 check(expr->getArg(0));
338 return true;
340 if (dc.Function("lastIndexOf").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
341 && fn->getNumParams() == 1)
343 if (expr->getNumArgs() >= 1)
345 check(expr->getArg(0));
347 return true;
349 return true;
352 bool preRun() override { return compiler.getLangOpts().CPlusPlus; }
354 private:
355 enum class Kind
357 OUStringFromLiteral,
358 StringViewOrVoid,
359 Other
362 void run() override
364 if (preRun())
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>
375 || tc.Void())
377 return Kind::StringViewOrVoid;
379 if (loplugin::TypeCheck(expr->getType())
380 .Class("OUString")
381 .Namespace("rtl")
382 .GlobalNamespace())
384 // Check for both explicit
386 // OUString("...")
388 // and implicit
390 // "..."
392 // expressions:
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;
407 return Kind::Other;
410 void check(Expr const* expr)
412 //TODO: skip `,`; handle ?: chains
413 auto const cond = dyn_cast<ConditionalOperator>(expr->IgnoreParenImpCasts());
414 if (cond == nullptr)
416 return;
418 auto const k1 = getKind(cond->getTrueExpr());
419 if (k1 == Kind::Other)
421 return;
423 auto const k2 = getKind(cond->getFalseExpr());
424 if (k2 == Kind::Other || (k1 == Kind::StringViewOrVoid && k2 == Kind::StringViewOrVoid))
426 return;
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`"),
433 cond->getExprLoc())
434 << cond->getSourceRange();
436 else
438 assert((k1 == Kind::OUStringFromLiteral && k2 == Kind::StringViewOrVoid)
439 || (k1 == Kind::StringViewOrVoid && k2 == Kind::OUStringFromLiteral));
440 auto const second = k1 == Kind::OUStringFromLiteral;
441 auto const sub
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`"),
446 sub->getExprLoc())
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");
457 #endif
459 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */