cURL: follow redirects
[LibreOffice.git] / compilerplugins / clang / stringconstant.cxx
blob9b9c934f589cedc0ed27ec3411eaf4bb15f20d89
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 #include <algorithm>
11 #include <cassert>
12 #include <cstdlib>
13 #include <limits>
14 #include <stack>
15 #include <string>
16 #include <iostream>
18 #include "check.hxx"
19 #include "plugin.hxx"
21 // Define a "string constant" to be a constant expression either of type "array
22 // of N char" where each array element is a non-NUL ASCII character---except
23 // that the last array element may be NUL, or, in some situations, of type char
24 // with a ASCII value (including NUL). Note that the former includes
25 // expressions denoting narrow string literals like "foo", and, with toolchains
26 // that support constexpr, constexpr variables declared like
28 // constexpr char str[] = "bar";
30 // This plugin flags uses of OUString functions with string constant arguments
31 // that can be rewritten more directly, like
33 // OUString::createFromAscii("foo") -> "foo"
35 // and
37 // s.equals(OUString("bar")) -> s == "bar"
39 namespace {
41 SourceLocation getMemberLocation(Expr const * expr) {
42 CallExpr const * e1 = dyn_cast<CallExpr>(expr);
43 MemberExpr const * e2 = e1 == nullptr
44 ? nullptr : dyn_cast<MemberExpr>(e1->getCallee());
45 return e2 == nullptr ? expr->getExprLoc()/*TODO*/ : e2->getMemberLoc();
48 bool isLhsOfAssignment(FunctionDecl const * decl, unsigned parameter) {
49 if (parameter != 0) {
50 return false;
52 auto oo = decl->getOverloadedOperator();
53 return oo == OO_Equal
54 || (oo >= OO_PlusEqual && oo <= OO_GreaterGreaterEqual);
57 bool hasOverloads(FunctionDecl const * decl, unsigned arguments) {
58 int n = 0;
59 auto ctx = decl->getDeclContext();
60 if (ctx->getDeclKind() == Decl::LinkageSpec) {
61 ctx = ctx->getParent();
63 auto res = ctx->lookup(decl->getDeclName());
64 for (auto d = res.begin(); d != res.end(); ++d) {
65 FunctionDecl const * f = dyn_cast<FunctionDecl>(*d);
66 if (f != nullptr && f->getMinRequiredArguments() <= arguments
67 && f->getNumParams() >= arguments)
69 ++n;
70 if (n == 2) {
71 return true;
75 return false;
78 class StringConstant:
79 public RecursiveASTVisitor<StringConstant>, public loplugin::RewritePlugin
81 public:
82 explicit StringConstant(InstantiationData const & data): RewritePlugin(data)
85 void run() override;
87 bool TraverseCallExpr(CallExpr * expr);
89 bool TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr);
91 bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr * expr);
93 bool TraverseCXXConstructExpr(CXXConstructExpr * expr);
95 bool VisitCallExpr(CallExpr const * expr);
97 bool VisitCXXConstructExpr(CXXConstructExpr const * expr);
99 private:
100 enum class TreatEmpty { DefaultCtor, CheckEmpty, Error };
102 enum class ChangeKind { Char, CharLen, SingleChar, OUStringLiteral1 };
104 enum class PassThrough { No, EmptyConstantString, NonEmptyConstantString };
106 std::string describeChangeKind(ChangeKind kind);
108 bool isStringConstant(
109 Expr const * expr, unsigned * size, bool * nonAscii,
110 bool * embeddedNuls, bool * terminatingNul);
112 bool isZero(Expr const * expr);
114 void reportChange(
115 Expr const * expr, ChangeKind kind, std::string const & original,
116 std::string const & replacement, PassThrough pass,
117 char const * rewriteFrom, char const * rewriteTo);
119 void checkEmpty(
120 CallExpr const * expr, FunctionDecl const * callee,
121 TreatEmpty treatEmpty, unsigned size, std::string * replacement);
123 void handleChar(
124 CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
125 std::string const & replacement, TreatEmpty treatEmpty, bool literal,
126 char const * rewriteFrom = nullptr, char const * rewriteTo = nullptr);
128 void handleCharLen(
129 CallExpr const * expr, unsigned arg1, unsigned arg2,
130 FunctionDecl const * callee, std::string const & replacement,
131 TreatEmpty treatEmpty);
133 void handleOUStringCtor(
134 CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
135 bool explicitFunctionalCastNotation);
137 std::stack<Expr const *> calls_;
140 void StringConstant::run() {
141 if (compiler.getLangOpts().CPlusPlus
142 && compiler.getPreprocessor().getIdentifierInfo(
143 "LIBO_INTERNAL_ONLY")->hasMacroDefinition())
144 //TODO: some parts of it are useful for external code, too
146 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
150 bool StringConstant::TraverseCallExpr(CallExpr * expr) {
151 if (!WalkUpFromCallExpr(expr)) {
152 return false;
154 calls_.push(expr);
155 bool bRes = true;
156 for (auto * e: expr->children()) {
157 if (!TraverseStmt(e)) {
158 bRes = false;
159 break;
162 calls_.pop();
163 return bRes;
166 bool StringConstant::TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr) {
167 if (!WalkUpFromCXXMemberCallExpr(expr)) {
168 return false;
170 calls_.push(expr);
171 bool bRes = true;
172 for (auto * e: expr->children()) {
173 if (!TraverseStmt(e)) {
174 bRes = false;
175 break;
178 calls_.pop();
179 return bRes;
182 bool StringConstant::TraverseCXXOperatorCallExpr(CXXOperatorCallExpr * expr)
184 if (!WalkUpFromCXXOperatorCallExpr(expr)) {
185 return false;
187 calls_.push(expr);
188 bool bRes = true;
189 for (auto * e: expr->children()) {
190 if (!TraverseStmt(e)) {
191 bRes = false;
192 break;
195 calls_.pop();
196 return bRes;
199 bool StringConstant::TraverseCXXConstructExpr(CXXConstructExpr * expr) {
200 if (!WalkUpFromCXXConstructExpr(expr)) {
201 return false;
203 calls_.push(expr);
204 bool bRes = true;
205 for (auto * e: expr->children()) {
206 if (!TraverseStmt(e)) {
207 bRes = false;
208 break;
211 calls_.pop();
212 return bRes;
215 bool StringConstant::VisitCallExpr(CallExpr const * expr) {
216 if (ignoreLocation(expr)) {
217 return true;
219 FunctionDecl const * fdecl = expr->getDirectCallee();
220 if (fdecl == nullptr) {
221 return true;
223 for (unsigned i = 0; i != fdecl->getNumParams(); ++i) {
224 auto t = fdecl->getParamDecl(i)->getType();
225 if (loplugin::TypeCheck(t).NotSubstTemplateTypeParmType()
226 .LvalueReference().Const().NotSubstTemplateTypeParmType()
227 .Class("OUString").Namespace("rtl").GlobalNamespace())
229 if (!(isLhsOfAssignment(fdecl, i)
230 || hasOverloads(fdecl, expr->getNumArgs())))
232 handleOUStringCtor(expr, i, fdecl, true);
236 loplugin::DeclCheck dc(fdecl);
237 //TODO: u.compareToAscii("foo") -> u.???("foo")
238 //TODO: u.compareToIgnoreAsciiCaseAscii("foo") -> u.???("foo")
239 if ((dc.Function("createFromAscii").Class("OUString").Namespace("rtl")
240 .GlobalNamespace())
241 && fdecl->getNumParams() == 1)
243 // OUString::createFromAscii("foo") -> OUString("foo")
244 handleChar(
245 expr, 0, fdecl, "rtl::OUString constructor",
246 TreatEmpty::DefaultCtor, true);
247 return true;
249 if ((dc.Function("endsWithAsciiL").Class("OUString").Namespace("rtl")
250 .GlobalNamespace())
251 && fdecl->getNumParams() == 2)
253 // u.endsWithAsciiL("foo", 3) -> u.endsWith("foo"):
254 handleCharLen(
255 expr, 0, 1, fdecl, "rtl::OUString::endsWith", TreatEmpty::Error);
256 return true;
258 if ((dc.Function("endsWithIgnoreAsciiCaseAsciiL").Class("OUString")
259 .Namespace("rtl").GlobalNamespace())
260 && fdecl->getNumParams() == 2)
262 // u.endsWithIgnoreAsciiCaseAsciiL("foo", 3) ->
263 // u.endsWithIgnoreAsciiCase("foo"):
264 handleCharLen(
265 expr, 0, 1, fdecl, "rtl::OUString::endsWithIgnoreAsciiCase",
266 TreatEmpty::Error);
267 return true;
269 if ((dc.Function("equalsAscii").Class("OUString").Namespace("rtl")
270 .GlobalNamespace())
271 && fdecl->getNumParams() == 1)
273 // u.equalsAscii("foo") -> u == "foo":
274 handleChar(
275 expr, 0, fdecl, "operator ==", TreatEmpty::CheckEmpty, false);
276 return true;
278 if ((dc.Function("equalsAsciiL").Class("OUString").Namespace("rtl")
279 .GlobalNamespace())
280 && fdecl->getNumParams() == 2)
282 // u.equalsAsciiL("foo", 3) -> u == "foo":
283 handleCharLen(expr, 0, 1, fdecl, "operator ==", TreatEmpty::CheckEmpty);
284 return true;
286 if ((dc.Function("equalsIgnoreAsciiCaseAscii").Class("OUString")
287 .Namespace("rtl").GlobalNamespace())
288 && fdecl->getNumParams() == 1)
290 // u.equalsIgnoreAsciiCaseAscii("foo") ->
291 // u.equalsIngoreAsciiCase("foo"):
292 handleChar(
293 expr, 0, fdecl, "rtl::OUString::equalsIgnoreAsciiCase",
294 TreatEmpty::CheckEmpty, false);
295 return true;
297 if ((dc.Function("equalsIgnoreAsciiCaseAsciiL").Class("OUString")
298 .Namespace("rtl").GlobalNamespace())
299 && fdecl->getNumParams() == 2)
301 // u.equalsIgnoreAsciiCaseAsciiL("foo", 3) ->
302 // u.equalsIngoreAsciiCase("foo"):
303 handleCharLen(
304 expr, 0, 1, fdecl, "rtl::OUString::equalsIgnoreAsciiCase",
305 TreatEmpty::CheckEmpty);
306 return true;
308 if ((dc.Function("indexOfAsciiL").Class("OUString").Namespace("rtl")
309 .GlobalNamespace())
310 && fdecl->getNumParams() == 3)
312 assert(expr->getNumArgs() == 3);
313 // u.indexOfAsciiL("foo", 3, i) -> u.indexOf("foo", i):
314 handleCharLen(
315 expr, 0, 1, fdecl, "rtl::OUString::indexOf", TreatEmpty::Error);
316 return true;
318 if ((dc.Function("lastIndexOfAsciiL").Class("OUString").Namespace("rtl")
319 .GlobalNamespace())
320 && fdecl->getNumParams() == 2)
322 // u.lastIndexOfAsciiL("foo", 3) -> u.lastIndexOf("foo"):
323 handleCharLen(
324 expr, 0, 1, fdecl, "rtl::OUString::lastIndexOf", TreatEmpty::Error);
325 return true;
327 if ((dc.Function("matchAsciiL").Class("OUString").Namespace("rtl")
328 .GlobalNamespace())
329 && fdecl->getNumParams() == 3)
331 assert(expr->getNumArgs() == 3);
332 // u.matchAsciiL("foo", 3, i) -> u.match("foo", i):
333 handleCharLen(
334 expr, 0, 1, fdecl,
335 (isZero(expr->getArg(2))
336 ? std::string("rtl::OUString::startsWith")
337 : std::string("rtl::OUString::match")),
338 TreatEmpty::Error);
339 return true;
341 if ((dc.Function("matchIgnoreAsciiCaseAsciiL").Class("OUString")
342 .Namespace("rtl").GlobalNamespace())
343 && fdecl->getNumParams() == 3)
345 assert(expr->getNumArgs() == 3);
346 // u.matchIgnoreAsciiCaseAsciiL("foo", 3, i) ->
347 // u.matchIgnoreAsciiCase("foo", i):
348 handleCharLen(
349 expr, 0, 1, fdecl,
350 (isZero(expr->getArg(2))
351 ? std::string("rtl::OUString::startsWithIgnoreAsciiCase")
352 : std::string("rtl::OUString::matchIgnoreAsciiCase")),
353 TreatEmpty::Error);
354 return true;
356 if ((dc.Function("reverseCompareToAsciiL").Class("OUString")
357 .Namespace("rtl").GlobalNamespace())
358 && fdecl->getNumParams() == 2)
360 // u.reverseCompareToAsciiL("foo", 3) -> u.reverseCompareTo("foo"):
361 handleCharLen(
362 expr, 0, 1, fdecl, "rtl::OUString::reverseCompareTo",
363 TreatEmpty::Error);
364 return true;
366 if ((dc.Function("reverseCompareTo").Class("OUString").Namespace("rtl")
367 .GlobalNamespace())
368 && fdecl->getNumParams() == 1)
370 handleOUStringCtor(expr, 0, fdecl, false);
371 return true;
373 if ((dc.Function("equalsIgnoreAsciiCase").Class("OUString").Namespace("rtl")
374 .GlobalNamespace())
375 && fdecl->getNumParams() == 1)
377 handleOUStringCtor(expr, 0, fdecl, false);
378 return true;
380 if ((dc.Function("match").Class("OUString").Namespace("rtl")
381 .GlobalNamespace())
382 && fdecl->getNumParams() == 2)
384 handleOUStringCtor(expr, 0, fdecl, false);
385 return true;
387 if ((dc.Function("matchIgnoreAsciiCase").Class("OUString").Namespace("rtl")
388 .GlobalNamespace())
389 && fdecl->getNumParams() == 2)
391 handleOUStringCtor(expr, 0, fdecl, false);
392 return true;
394 if ((dc.Function("startsWith").Class("OUString").Namespace("rtl")
395 .GlobalNamespace())
396 && fdecl->getNumParams() == 2)
398 handleOUStringCtor(expr, 0, fdecl, false);
399 return true;
401 if ((dc.Function("startsWithIgnoreAsciiCase").Class("OUString")
402 .Namespace("rtl").GlobalNamespace())
403 && fdecl->getNumParams() == 2)
405 handleOUStringCtor(expr, 0, fdecl, false);
406 return true;
408 if ((dc.Function("endsWith").Class("OUString").Namespace("rtl")
409 .GlobalNamespace())
410 && fdecl->getNumParams() == 2)
412 handleOUStringCtor(expr, 0, fdecl, false);
413 return true;
415 if ((dc.Function("endsWithIgnoreAsciiCase").Class("OUString")
416 .Namespace("rtl").GlobalNamespace())
417 && fdecl->getNumParams() == 2)
419 handleOUStringCtor(expr, 0, fdecl, false);
420 return true;
422 if ((dc.Function("indexOf").Class("OUString").Namespace("rtl")
423 .GlobalNamespace())
424 && fdecl->getNumParams() == 2)
426 handleOUStringCtor(expr, 0, fdecl, false);
427 return true;
429 if ((dc.Function("lastIndexOf").Class("OUString").Namespace("rtl")
430 .GlobalNamespace())
431 && fdecl->getNumParams() == 1)
433 handleOUStringCtor(expr, 0, fdecl, false);
434 return true;
436 if ((dc.Function("replaceFirst").Class("OUString").Namespace("rtl")
437 .GlobalNamespace())
438 && fdecl->getNumParams() == 3)
440 handleOUStringCtor(expr, 0, fdecl, false);
441 handleOUStringCtor(expr, 1, fdecl, false);
442 return true;
444 if ((dc.Function("replaceAll").Class("OUString").Namespace("rtl")
445 .GlobalNamespace())
446 && (fdecl->getNumParams() == 2 || fdecl->getNumParams() == 3))
448 handleOUStringCtor(expr, 0, fdecl, false);
449 handleOUStringCtor(expr, 1, fdecl, false);
450 return true;
452 if ((dc.Operator(OO_PlusEqual).Class("OUString").Namespace("rtl")
453 .GlobalNamespace())
454 && fdecl->getNumParams() == 1)
456 handleOUStringCtor(
457 expr, dyn_cast<CXXOperatorCallExpr>(expr) == nullptr ? 0 : 1,
458 fdecl, false);
459 return true;
461 if ((dc.Function("equals").Class("OUString").Namespace("rtl")
462 .GlobalNamespace())
463 && fdecl->getNumParams() == 1)
465 unsigned n;
466 bool non;
467 bool emb;
468 bool trm;
469 if (!isStringConstant(
470 expr->getArg(0)->IgnoreParenImpCasts(), &n, &non, &emb, &trm))
472 return true;
474 if (non) {
475 report(
476 DiagnosticsEngine::Warning,
477 ("call of %0 with string constant argument containging"
478 " non-ASCII characters"),
479 expr->getExprLoc())
480 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
482 if (emb) {
483 report(
484 DiagnosticsEngine::Warning,
485 ("call of %0 with string constant argument containging embedded"
486 " NULs"),
487 expr->getExprLoc())
488 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
490 if (n == 0) {
491 report(
492 DiagnosticsEngine::Warning,
493 ("rewrite call of %0 with empty string constant argument as"
494 " call of rtl::OUString::isEmpty"),
495 expr->getExprLoc())
496 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
497 return true;
500 if (dc.Operator(OO_EqualEqual).Namespace("rtl").GlobalNamespace()
501 && fdecl->getNumParams() == 2)
503 for (unsigned i = 0; i != 2; ++i) {
504 unsigned n;
505 bool non;
506 bool emb;
507 bool trm;
508 if (!isStringConstant(
509 expr->getArg(i)->IgnoreParenImpCasts(), &n, &non, &emb,
510 &trm))
512 continue;
514 if (non) {
515 report(
516 DiagnosticsEngine::Warning,
517 ("call of %0 with string constant argument containging"
518 " non-ASCII characters"),
519 expr->getExprLoc())
520 << fdecl->getQualifiedNameAsString()
521 << expr->getSourceRange();
523 if (emb) {
524 report(
525 DiagnosticsEngine::Warning,
526 ("call of %0 with string constant argument containging"
527 " embedded NULs"),
528 expr->getExprLoc())
529 << fdecl->getQualifiedNameAsString()
530 << expr->getSourceRange();
532 if (n == 0) {
533 report(
534 DiagnosticsEngine::Warning,
535 ("rewrite call of %0 with empty string constant argument as"
536 " call of rtl::OUString::isEmpty"),
537 expr->getExprLoc())
538 << fdecl->getQualifiedNameAsString()
539 << expr->getSourceRange();
542 return true;
544 if (dc.Operator(OO_ExclaimEqual).Namespace("rtl").GlobalNamespace()
545 && fdecl->getNumParams() == 2)
547 for (unsigned i = 0; i != 2; ++i) {
548 unsigned n;
549 bool non;
550 bool emb;
551 bool trm;
552 if (!isStringConstant(
553 expr->getArg(i)->IgnoreParenImpCasts(), &n, &non, &emb,
554 &trm))
556 continue;
558 if (non) {
559 report(
560 DiagnosticsEngine::Warning,
561 ("call of %0 with string constant argument containging"
562 " non-ASCII characters"),
563 expr->getExprLoc())
564 << fdecl->getQualifiedNameAsString()
565 << expr->getSourceRange();
567 if (emb) {
568 report(
569 DiagnosticsEngine::Warning,
570 ("call of %0 with string constant argument containging"
571 " embedded NULs"),
572 expr->getExprLoc())
573 << fdecl->getQualifiedNameAsString()
574 << expr->getSourceRange();
576 if (n == 0) {
577 report(
578 DiagnosticsEngine::Warning,
579 ("rewrite call of %0 with empty string constant argument as"
580 " call of !rtl::OUString::isEmpty"),
581 expr->getExprLoc())
582 << fdecl->getQualifiedNameAsString()
583 << expr->getSourceRange();
586 return true;
588 if (dc.Operator(OO_Equal).Namespace("rtl").GlobalNamespace()
589 && fdecl->getNumParams() == 1)
591 unsigned n;
592 bool non;
593 bool emb;
594 bool trm;
595 if (!isStringConstant(
596 expr->getArg(1)->IgnoreParenImpCasts(), &n, &non, &emb, &trm))
598 return true;
600 if (non) {
601 report(
602 DiagnosticsEngine::Warning,
603 ("call of %0 with string constant argument containging"
604 " non-ASCII characters"),
605 expr->getExprLoc())
606 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
608 if (emb) {
609 report(
610 DiagnosticsEngine::Warning,
611 ("call of %0 with string constant argument containging embedded"
612 " NULs"),
613 expr->getExprLoc())
614 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
616 if (n == 0) {
617 report(
618 DiagnosticsEngine::Warning,
619 ("rewrite call of %0 with empty string constant argument as"
620 " call of rtl::OUString::clear"),
621 expr->getExprLoc())
622 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
623 return true;
625 return true;
627 if ((dc.Function("appendAscii").Class("OUStringBuffer").Namespace("rtl")
628 .GlobalNamespace())
629 && fdecl->getNumParams() == 1)
631 // u.appendAscii("foo") -> u.append("foo")
632 handleChar(
633 expr, 0, fdecl, "rtl::OUStringBuffer::append", TreatEmpty::Error,
634 true, "appendAscii", "append");
635 return true;
637 if ((dc.Function("appendAscii").Class("OUStringBuffer").Namespace("rtl")
638 .GlobalNamespace())
639 && fdecl->getNumParams() == 2)
641 // u.appendAscii("foo", 3) -> u.append("foo"):
642 handleCharLen(
643 expr, 0, 1, fdecl, "rtl::OUStringBuffer::append",
644 TreatEmpty::Error);
645 return true;
647 return true;
650 bool StringConstant::VisitCXXConstructExpr(CXXConstructExpr const * expr) {
651 if (ignoreLocation(expr)) {
652 return true;
654 auto cdecl = expr->getConstructor()->getParent();
655 if (loplugin::DeclCheck(cdecl)
656 .Class("OUString").Namespace("rtl").GlobalNamespace())
658 ChangeKind kind;
659 PassThrough pass;
660 switch (expr->getConstructor()->getNumParams()) {
661 case 1:
662 if (!loplugin::TypeCheck(
663 expr->getConstructor()->getParamDecl(0)->getType())
664 .Typedef("sal_Unicode").GlobalNamespace())
666 return true;
668 kind = ChangeKind::SingleChar;
669 pass = PassThrough::NonEmptyConstantString;
670 break;
671 case 2:
673 auto arg = expr->getArg(0);
674 if (loplugin::TypeCheck(arg->getType())
675 .Class("OUStringLiteral1_").Namespace("rtl")
676 .GlobalNamespace())
678 kind = ChangeKind::OUStringLiteral1;
679 pass = PassThrough::NonEmptyConstantString;
680 } else {
681 unsigned n;
682 bool non;
683 bool emb;
684 bool trm;
685 if (!isStringConstant(
686 arg->IgnoreParenImpCasts(), &n, &non, &emb, &trm))
688 return true;
690 if (non) {
691 report(
692 DiagnosticsEngine::Warning,
693 ("construction of %0 with string constant argument"
694 " containging non-ASCII characters"),
695 expr->getExprLoc())
696 << cdecl << expr->getSourceRange();
698 if (emb) {
699 report(
700 DiagnosticsEngine::Warning,
701 ("construction of %0 with string constant argument"
702 " containging embedded NULs"),
703 expr->getExprLoc())
704 << cdecl << expr->getSourceRange();
706 kind = ChangeKind::Char;
707 pass = n == 0
708 ? PassThrough::EmptyConstantString
709 : PassThrough::NonEmptyConstantString;
711 break;
713 default:
714 return true;
716 if (!calls_.empty()) {
717 Expr const * call = calls_.top();
718 CallExpr::const_arg_iterator argsBeg;
719 CallExpr::const_arg_iterator argsEnd;
720 if (isa<CallExpr>(call)) {
721 argsBeg = cast<CallExpr>(call)->arg_begin();
722 argsEnd = cast<CallExpr>(call)->arg_end();
723 } else if (isa<CXXConstructExpr>(call)) {
724 argsBeg = cast<CXXConstructExpr>(call)->arg_begin();
725 argsEnd = cast<CXXConstructExpr>(call)->arg_end();
726 } else {
727 assert(false);
729 for (auto i(argsBeg); i != argsEnd; ++i) {
730 Expr const * e = (*i)->IgnoreParenImpCasts();
731 if (isa<MaterializeTemporaryExpr>(e)) {
732 e = cast<MaterializeTemporaryExpr>(e)->GetTemporaryExpr()
733 ->IgnoreParenImpCasts();
735 if (isa<CXXFunctionalCastExpr>(e)) {
736 e = cast<CXXFunctionalCastExpr>(e)->getSubExpr()
737 ->IgnoreParenImpCasts();
739 if (isa<CXXBindTemporaryExpr>(e)) {
740 e = cast<CXXBindTemporaryExpr>(e)->getSubExpr()
741 ->IgnoreParenImpCasts();
743 if (e == expr) {
744 if (isa<CallExpr>(call)) {
745 FunctionDecl const * fdecl
746 = cast<CallExpr>(call)->getDirectCallee();
747 if (fdecl == nullptr) {
748 break;
750 loplugin::DeclCheck dc(fdecl);
751 if (pass == PassThrough::EmptyConstantString) {
752 if ((dc.Function("equals").Class("OUString")
753 .Namespace("rtl").GlobalNamespace())
754 || (dc.Operator(OO_EqualEqual).Namespace("rtl")
755 .GlobalNamespace()))
757 report(
758 DiagnosticsEngine::Warning,
759 ("rewrite call of %0 with construction of"
760 " %1 with empty string constant argument"
761 " as call of rtl::OUString::isEmpty"),
762 getMemberLocation(call))
763 << fdecl->getQualifiedNameAsString()
764 << cdecl << call->getSourceRange();
765 return true;
767 if (dc.Operator(OO_ExclaimEqual).Namespace("rtl")
768 .GlobalNamespace())
770 report(
771 DiagnosticsEngine::Warning,
772 ("rewrite call of %0 with construction of"
773 " %1 with empty string constant argument"
774 " as call of !rtl::OUString::isEmpty"),
775 getMemberLocation(call))
776 << fdecl->getQualifiedNameAsString()
777 << cdecl << call->getSourceRange();
778 return true;
780 if ((dc.Operator(OO_Plus).Namespace("rtl")
781 .GlobalNamespace())
782 || (dc.Operator(OO_Plus).Class("OUString")
783 .Namespace("rtl").GlobalNamespace()))
785 report(
786 DiagnosticsEngine::Warning,
787 ("call of %0 with suspicous construction of"
788 " %1 with empty string constant argument"),
789 getMemberLocation(call))
790 << fdecl->getQualifiedNameAsString()
791 << cdecl << call->getSourceRange();
792 return true;
794 if (dc.Operator(OO_Equal).Class("OUString")
795 .Namespace("rtl").GlobalNamespace())
797 report(
798 DiagnosticsEngine::Warning,
799 ("rewrite call of %0 with construction of"
800 " %1 with empty string constant argument"
801 " as call of rtl::OUString::clear"),
802 getMemberLocation(call))
803 << fdecl->getQualifiedNameAsString()
804 << cdecl << call->getSourceRange();
805 return true;
807 } else {
808 assert(pass == PassThrough::NonEmptyConstantString);
809 if (dc.Function("equals").Class("OUString")
810 .Namespace("rtl").GlobalNamespace())
812 report(
813 DiagnosticsEngine::Warning,
814 (("rewrite call of %0 with construction of"
815 " %1 with ")
816 + describeChangeKind(kind)
817 + " as operator =="),
818 getMemberLocation(call))
819 << fdecl->getQualifiedNameAsString()
820 << cdecl << call->getSourceRange();
821 return true;
823 if ((dc.Operator(OO_Plus).Namespace("rtl")
824 .GlobalNamespace())
825 || (dc.Operator(OO_Plus).Class("OUString")
826 .Namespace("rtl").GlobalNamespace())
827 || (dc.Operator(OO_EqualEqual).Namespace("rtl")
828 .GlobalNamespace())
829 || (dc.Operator(OO_ExclaimEqual)
830 .Namespace("rtl").GlobalNamespace()))
832 if (dc.Operator(OO_Plus).Namespace("rtl")
833 .GlobalNamespace())
835 std::string file(
836 compiler.getSourceManager().getFilename(
837 compiler.getSourceManager()
838 .getSpellingLoc(
839 expr->getLocStart())));
840 if (file
841 == (SRCDIR
842 "/sal/qa/rtl/strings/test_ostring_concat.cxx")
843 || (file
844 == (SRCDIR
845 "/sal/qa/rtl/strings/test_oustring_concat.cxx")))
847 return true;
850 auto loc = expr->getArg(0)->getLocStart();
851 while (compiler.getSourceManager()
852 .isMacroArgExpansion(loc))
854 loc = compiler.getSourceManager()
855 .getImmediateMacroCallerLoc(loc);
857 if ((compiler.getSourceManager()
858 .isMacroBodyExpansion(loc))
859 && (Lexer::getImmediateMacroName(
860 loc, compiler.getSourceManager(),
861 compiler.getLangOpts())
862 == "OSL_THIS_FUNC"))
864 return true;
866 if (kind == ChangeKind::SingleChar) {
867 report(
868 DiagnosticsEngine::Warning,
869 ("rewrite construction of %0 with "
870 + describeChangeKind(kind)
871 + (" in call of %1 as construction of"
872 " OUStringLiteral1")),
873 getMemberLocation(expr))
874 << cdecl
875 << fdecl->getQualifiedNameAsString()
876 << expr->getSourceRange();
877 } else {
878 report(
879 DiagnosticsEngine::Warning,
880 ("elide construction of %0 with "
881 + describeChangeKind(kind)
882 + " in call of %1"),
883 getMemberLocation(expr))
884 << cdecl
885 << fdecl->getQualifiedNameAsString()
886 << expr->getSourceRange();
888 return true;
891 return true;
892 } else if (isa<CXXConstructExpr>(call)) {
893 } else {
894 assert(false);
899 return true;
901 return true;
904 std::string StringConstant::describeChangeKind(ChangeKind kind) {
905 switch (kind) {
906 case ChangeKind::Char:
907 return "string constant argument";
908 case ChangeKind::CharLen:
909 return "string constant and matching length arguments";
910 case ChangeKind::SingleChar:
911 return "sal_Unicode argument";
912 case ChangeKind::OUStringLiteral1:
913 return "OUStringLiteral1 argument";
914 default:
915 std::abort();
919 bool StringConstant::isStringConstant(
920 Expr const * expr, unsigned * size, bool * nonAscii, bool * embeddedNuls,
921 bool * terminatingNul)
923 assert(expr != nullptr);
924 assert(size != nullptr);
925 assert(nonAscii != nullptr);
926 assert(embeddedNuls != nullptr);
927 assert(terminatingNul != nullptr);
928 QualType t = expr->getType();
929 // Look inside RTL_CONSTASCII_STRINGPARAM:
930 if (loplugin::TypeCheck(t).Pointer().Const().Char()) {
931 auto e2 = dyn_cast<UnaryOperator>(expr);
932 if (e2 == nullptr || e2->getOpcode() != UO_AddrOf) {
933 return false;
935 auto e3 = dyn_cast<ArraySubscriptExpr>(
936 e2->getSubExpr()->IgnoreParenImpCasts());
937 if (e3 == nullptr || !isZero(e3->getIdx()->IgnoreParenImpCasts())) {
938 return false;
940 expr = e3->getBase()->IgnoreParenImpCasts();
941 t = expr->getType();
943 if (!(t->isConstantArrayType() && t.isConstQualified()
944 && (loplugin::TypeCheck(t->getAsArrayTypeUnsafe()->getElementType())
945 .Char())))
947 return false;
949 DeclRefExpr const * dre = dyn_cast<DeclRefExpr>(expr);
950 if (dre != nullptr) {
951 VarDecl const * var = dyn_cast<VarDecl>(dre->getDecl());
952 if (var != nullptr) {
953 Expr const * init = var->getAnyInitializer();
954 if (init != nullptr) {
955 expr = init->IgnoreParenImpCasts();
959 StringLiteral const * lit = dyn_cast<StringLiteral>(expr);
960 if (lit != nullptr) {
961 if (!lit->isAscii()) {
962 return false;
964 unsigned n = lit->getLength();
965 bool non = false;
966 bool emb = false;
967 StringRef str = lit->getString();
968 for (unsigned i = 0; i != n; ++i) {
969 if (str[i] == '\0') {
970 emb = true;
971 } else if (static_cast<unsigned char>(str[i]) >= 0x80) {
972 non = true;
975 *size = n;
976 *nonAscii = non;
977 *embeddedNuls = emb;
978 *terminatingNul = true;
979 return true;
981 APValue v;
982 if (!expr->isCXX11ConstantExpr(compiler.getASTContext(), &v)) {
983 return false;
985 switch (v.getKind()) {
986 case APValue::LValue:
988 Expr const * e = v.getLValueBase().dyn_cast<Expr const *>();
989 assert(e != nullptr); //TODO???
990 if (!v.getLValueOffset().isZero()) {
991 return false; //TODO
993 Expr const * e2 = e->IgnoreParenImpCasts();
994 if (e2 != e) {
995 return isStringConstant(
996 e2, size, nonAscii, embeddedNuls, terminatingNul);
998 //TODO: string literals are represented as recursive LValues???
999 llvm::APInt n
1000 = compiler.getASTContext().getAsConstantArrayType(t)->getSize();
1001 assert(n != 0);
1002 --n;
1003 assert(n.ule(std::numeric_limits<unsigned>::max()));
1004 *size = static_cast<unsigned>(n.getLimitedValue());
1005 *nonAscii = false; //TODO
1006 *embeddedNuls = false; //TODO
1007 *terminatingNul = true;
1008 return true;
1010 case APValue::Array:
1012 if (v.hasArrayFiller()) { //TODO: handle final NUL filler?
1013 return false;
1015 unsigned n = v.getArraySize();
1016 assert(n != 0);
1017 bool non = false;
1018 bool emb = false;
1019 for (unsigned i = 0; i != n - 1; ++i) {
1020 APValue e(v.getArrayInitializedElt(i));
1021 if (!e.isInt()) { //TODO: assert?
1022 return false;
1024 APSInt iv = e.getInt();
1025 if (iv == 0) {
1026 emb = true;
1027 } else if (iv.uge(0x80)) {
1028 non = true;
1031 APValue e(v.getArrayInitializedElt(n - 1));
1032 if (!e.isInt()) { //TODO: assert?
1033 return false;
1035 bool trm = e.getInt() == 0;
1036 *size = trm ? n - 1 : n;
1037 *nonAscii = non;
1038 *embeddedNuls = emb;
1039 *terminatingNul = trm;
1040 return true;
1042 default:
1043 assert(false); //TODO???
1044 return false;
1048 bool StringConstant::isZero(Expr const * expr) {
1049 APSInt res;
1050 return expr->isIntegerConstantExpr(res, compiler.getASTContext())
1051 && res == 0;
1054 void StringConstant::reportChange(
1055 Expr const * expr, ChangeKind kind, std::string const & original,
1056 std::string const & replacement, PassThrough pass, char const * rewriteFrom,
1057 char const * rewriteTo)
1059 assert((rewriteFrom == nullptr) == (rewriteTo == nullptr));
1060 if (pass != PassThrough::No && !calls_.empty()) {
1061 Expr const * call = calls_.top();
1062 CallExpr::const_arg_iterator argsBeg;
1063 CallExpr::const_arg_iterator argsEnd;
1064 if (isa<CallExpr>(call)) {
1065 argsBeg = cast<CallExpr>(call)->arg_begin();
1066 argsEnd = cast<CallExpr>(call)->arg_end();
1067 } else if (isa<CXXConstructExpr>(call)) {
1068 argsBeg = cast<CXXConstructExpr>(call)->arg_begin();
1069 argsEnd = cast<CXXConstructExpr>(call)->arg_end();
1070 } else {
1071 assert(false);
1073 for (auto i(argsBeg); i != argsEnd; ++i) {
1074 Expr const * e = (*i)->IgnoreParenImpCasts();
1075 if (isa<CXXBindTemporaryExpr>(e)) {
1076 e = cast<CXXBindTemporaryExpr>(e)->getSubExpr()
1077 ->IgnoreParenImpCasts();
1079 if (e == expr) {
1080 if (isa<CallExpr>(call)) {
1081 FunctionDecl const * fdecl
1082 = cast<CallExpr>(call)->getDirectCallee();
1083 if (fdecl == nullptr) {
1084 break;
1086 loplugin::DeclCheck dc(fdecl);
1087 if (pass == PassThrough::EmptyConstantString) {
1088 if ((dc.Function("equals").Class("OUString")
1089 .Namespace("rtl").GlobalNamespace())
1090 || (dc.Operator(OO_EqualEqual).Namespace("rtl")
1091 .GlobalNamespace()))
1093 report(
1094 DiagnosticsEngine::Warning,
1095 ("rewrite call of %0 with call of " + original
1096 + (" with empty string constant argument as"
1097 " call of rtl::OUString::isEmpty")),
1098 getMemberLocation(call))
1099 << fdecl->getQualifiedNameAsString()
1100 << call->getSourceRange();
1101 return;
1103 if (dc.Operator(OO_ExclaimEqual).Namespace("rtl")
1104 .GlobalNamespace())
1106 report(
1107 DiagnosticsEngine::Warning,
1108 ("rewrite call of %0 with call of " + original
1109 + (" with empty string constant argument as"
1110 " call of !rtl::OUString::isEmpty")),
1111 getMemberLocation(call))
1112 << fdecl->getQualifiedNameAsString()
1113 << call->getSourceRange();
1114 return;
1116 if ((dc.Operator(OO_Plus).Namespace("rtl")
1117 .GlobalNamespace())
1118 || (dc.Operator(OO_Plus).Class("OUString")
1119 .Namespace("rtl").GlobalNamespace()))
1121 report(
1122 DiagnosticsEngine::Warning,
1123 ("call of %0 with suspicous call of " + original
1124 + " with empty string constant argument"),
1125 getMemberLocation(call))
1126 << fdecl->getQualifiedNameAsString()
1127 << call->getSourceRange();
1128 return;
1130 if (dc.Operator(OO_Equal).Class("OUString")
1131 .Namespace("rtl").GlobalNamespace())
1133 report(
1134 DiagnosticsEngine::Warning,
1135 ("rewrite call of %0 with call of " + original
1136 + (" with empty string constant argument as"
1137 " call of rtl::OUString::call")),
1138 getMemberLocation(call))
1139 << fdecl->getQualifiedNameAsString()
1140 << call->getSourceRange();
1141 return;
1143 } else {
1144 assert(pass == PassThrough::NonEmptyConstantString);
1145 if ((dc.Function("equals").Class("OUString")
1146 .Namespace("rtl").GlobalNamespace())
1147 || (dc.Operator(OO_Equal).Class("OUString")
1148 .Namespace("rtl").GlobalNamespace())
1149 || (dc.Operator(OO_EqualEqual).Namespace("rtl")
1150 .GlobalNamespace())
1151 || (dc.Operator(OO_ExclaimEqual).Namespace("rtl")
1152 .GlobalNamespace()))
1154 report(
1155 DiagnosticsEngine::Warning,
1156 ("elide call of " + original + " with "
1157 + describeChangeKind(kind) + " in call of %0"),
1158 getMemberLocation(expr))
1159 << fdecl->getQualifiedNameAsString()
1160 << expr->getSourceRange();
1161 return;
1163 if ((dc.Operator(OO_Plus).Namespace("rtl")
1164 .GlobalNamespace())
1165 || (dc.Operator(OO_Plus).Class("OUString")
1166 .Namespace("rtl").GlobalNamespace()))
1168 report(
1169 DiagnosticsEngine::Warning,
1170 ("rewrite call of " + original + " with "
1171 + describeChangeKind(kind)
1172 + (" in call of %0 as (implicit) construction"
1173 " of rtl::OUString")),
1174 getMemberLocation(expr))
1175 << fdecl->getQualifiedNameAsString()
1176 << expr->getSourceRange();
1177 return;
1180 report(
1181 DiagnosticsEngine::Warning,
1182 "TODO call inside %0", getMemberLocation(expr))
1183 << fdecl->getQualifiedNameAsString()
1184 << expr->getSourceRange();
1185 return;
1186 } else if (isa<CXXConstructExpr>(call)) {
1187 auto cdecl = cast<CXXConstructExpr>(call)->getConstructor()
1188 ->getParent();
1189 loplugin::DeclCheck dc(cdecl);
1190 if (dc.Class("OUString").Namespace("rtl").GlobalNamespace()
1191 || (dc.Class("OUStringBuffer").Namespace("rtl")
1192 .GlobalNamespace()))
1194 //TODO: propagate further out?
1195 if (pass == PassThrough::EmptyConstantString) {
1196 report(
1197 DiagnosticsEngine::Warning,
1198 ("rewrite construction of %0 with call of "
1199 + original
1200 + (" with empty string constant argument as"
1201 " default construction of %0")),
1202 getMemberLocation(call))
1203 << cdecl->getQualifiedNameAsString()
1204 << call->getSourceRange();
1205 } else {
1206 assert(pass == PassThrough::NonEmptyConstantString);
1207 report(
1208 DiagnosticsEngine::Warning,
1209 ("elide call of " + original + " with "
1210 + describeChangeKind(kind)
1211 + " in construction of %0"),
1212 getMemberLocation(expr))
1213 << cdecl->getQualifiedNameAsString()
1214 << expr->getSourceRange();
1216 return;
1218 } else {
1219 assert(false);
1224 if (rewriter != nullptr && rewriteFrom != nullptr) {
1225 SourceLocation loc = getMemberLocation(expr);
1226 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
1227 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
1229 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
1230 loc = compiler.getSourceManager().getSpellingLoc(loc);
1232 unsigned n = Lexer::MeasureTokenLength(
1233 loc, compiler.getSourceManager(), compiler.getLangOpts());
1234 if ((std::string(compiler.getSourceManager().getCharacterData(loc), n)
1235 == rewriteFrom)
1236 && replaceText(loc, n, rewriteTo))
1238 return;
1241 report(
1242 DiagnosticsEngine::Warning,
1243 ("rewrite call of " + original + " with " + describeChangeKind(kind)
1244 + " as call of " + replacement),
1245 getMemberLocation(expr))
1246 << expr->getSourceRange();
1249 void StringConstant::checkEmpty(
1250 CallExpr const * expr, FunctionDecl const * callee, TreatEmpty treatEmpty,
1251 unsigned size, std::string * replacement)
1253 assert(replacement != nullptr);
1254 if (size == 0) {
1255 switch (treatEmpty) {
1256 case TreatEmpty::DefaultCtor:
1257 *replacement = "rtl::OUString default constructor";
1258 break;
1259 case TreatEmpty::CheckEmpty:
1260 *replacement = "rtl::OUString::isEmpty";
1261 break;
1262 case TreatEmpty::Error:
1263 report(
1264 DiagnosticsEngine::Warning,
1265 "call of %0 with suspicous empty string constant argument",
1266 getMemberLocation(expr))
1267 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1268 break;
1273 void StringConstant::handleChar(
1274 CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
1275 std::string const & replacement, TreatEmpty treatEmpty, bool literal,
1276 char const * rewriteFrom, char const * rewriteTo)
1278 unsigned n;
1279 bool non;
1280 bool emb;
1281 bool trm;
1282 if (!isStringConstant(
1283 expr->getArg(arg)->IgnoreParenImpCasts(), &n, &non, &emb, &trm))
1285 return;
1287 if (non) {
1288 report(
1289 DiagnosticsEngine::Warning,
1290 ("call of %0 with string constant argument containging non-ASCII"
1291 " characters"),
1292 getMemberLocation(expr))
1293 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1294 return;
1296 if (emb) {
1297 report(
1298 DiagnosticsEngine::Warning,
1299 ("call of %0 with string constant argument containging embedded"
1300 " NULs"),
1301 getMemberLocation(expr))
1302 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1303 return;
1305 if (!trm) {
1306 report(
1307 DiagnosticsEngine::Warning,
1308 ("call of %0 with string constant argument lacking a terminating"
1309 " NUL"),
1310 getMemberLocation(expr))
1311 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1312 return;
1314 std::string repl(replacement);
1315 checkEmpty(expr, callee, treatEmpty, n, &repl);
1316 reportChange(
1317 expr, ChangeKind::Char, callee->getQualifiedNameAsString(), repl,
1318 (literal
1319 ? (n == 0
1320 ? PassThrough::EmptyConstantString
1321 : PassThrough::NonEmptyConstantString)
1322 : PassThrough::No),
1323 rewriteFrom, rewriteTo);
1326 void StringConstant::handleCharLen(
1327 CallExpr const * expr, unsigned arg1, unsigned arg2,
1328 FunctionDecl const * callee, std::string const & replacement,
1329 TreatEmpty treatEmpty)
1331 // Especially for f(RTL_CONSTASCII_STRINGPARAM("foo")), where
1332 // RTL_CONSTASCII_STRINGPARAM expands to complicated expressions involving
1333 // (&(X)[0] sub-expressions (and it might or might not be better to handle
1334 // that at the level of non-expanded macros instead, but I have not found
1335 // out how to do that yet anyway):
1336 unsigned n;
1337 bool non;
1338 bool emb;
1339 bool trm;
1340 if (!(isStringConstant(
1341 expr->getArg(arg1)->IgnoreParenImpCasts(), &n, &non, &emb, &trm)
1342 && trm))
1344 return;
1346 APSInt res;
1347 if (expr->getArg(arg2)->isIntegerConstantExpr(
1348 res, compiler.getASTContext()))
1350 if (res != n) {
1351 return;
1353 } else {
1354 UnaryOperator const * op = dyn_cast<UnaryOperator>(
1355 expr->getArg(arg1)->IgnoreParenImpCasts());
1356 if (op == nullptr || op->getOpcode() != UO_AddrOf) {
1357 return;
1359 ArraySubscriptExpr const * subs = dyn_cast<ArraySubscriptExpr>(
1360 op->getSubExpr()->IgnoreParenImpCasts());
1361 if (subs == nullptr) {
1362 return;
1364 unsigned n2;
1365 bool non2;
1366 bool emb2;
1367 bool trm2;
1368 if (!(isStringConstant(
1369 subs->getBase()->IgnoreParenImpCasts(), &n2, &non2, &emb2,
1370 &trm2)
1371 && n2 == n && non2 == non && emb2 == emb && trm2 == trm
1372 //TODO: same strings
1373 && subs->getIdx()->isIntegerConstantExpr(
1374 res, compiler.getASTContext())
1375 && res == 0))
1377 return;
1380 if (non) {
1381 report(
1382 DiagnosticsEngine::Warning,
1383 ("call of %0 with string constant argument containging non-ASCII"
1384 " characters"),
1385 getMemberLocation(expr))
1386 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1388 if (emb) {
1389 return;
1391 std::string repl(replacement);
1392 checkEmpty(expr, callee, treatEmpty, n, &repl);
1393 reportChange(
1394 expr, ChangeKind::CharLen, callee->getQualifiedNameAsString(), repl,
1395 PassThrough::No, nullptr, nullptr);
1398 void StringConstant::handleOUStringCtor(
1399 CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
1400 bool explicitFunctionalCastNotation)
1402 auto e0 = expr->getArg(arg)->IgnoreParenImpCasts();
1403 auto e1 = dyn_cast<CXXFunctionalCastExpr>(e0);
1404 if (e1 == nullptr) {
1405 if (explicitFunctionalCastNotation) {
1406 return;
1408 } else {
1409 e0 = e1->getSubExpr()->IgnoreParenImpCasts();
1411 auto e2 = dyn_cast<CXXBindTemporaryExpr>(e0);
1412 if (e2 == nullptr) {
1413 return;
1415 auto e3 = dyn_cast<CXXConstructExpr>(
1416 e2->getSubExpr()->IgnoreParenImpCasts());
1417 if (e3 == nullptr) {
1418 return;
1420 if (e3->getConstructor()->getQualifiedNameAsString()
1421 != "rtl::OUString::OUString")
1423 return;
1425 if (e3->getNumArgs() == 0) {
1426 report(
1427 DiagnosticsEngine::Warning,
1428 ("in call of %0, replace default-constructed OUString with an empty"
1429 " string literal"),
1430 e3->getExprLoc())
1431 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1432 return;
1434 if (e3->getNumArgs() == 1
1435 && e3->getConstructor()->getNumParams() == 1
1436 && (loplugin::TypeCheck(
1437 e3->getConstructor()->getParamDecl(0)->getType())
1438 .Typedef("sal_Unicode").GlobalNamespace()))
1440 // It may not be easy to rewrite OUString(c), esp. given there is no
1441 // OUString ctor taking an OUStringLiteral1 arg, so don't warn there:
1442 if (!explicitFunctionalCastNotation) {
1443 report(
1444 DiagnosticsEngine::Warning,
1445 ("in call of %0, replace OUString constructed from a"
1446 " sal_Unicode with an OUStringLiteral1"),
1447 e3->getExprLoc())
1448 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1450 return;
1452 if (e3->getNumArgs() != 2) {
1453 return;
1455 unsigned n;
1456 bool non;
1457 bool emb;
1458 bool trm;
1459 if (!isStringConstant(
1460 e3->getArg(0)->IgnoreParenImpCasts(), &n, &non, &emb, &trm))
1462 return;
1464 //TODO: non, emb, trm
1465 if (rewriter != nullptr) {
1466 auto loc1 = e3->getLocStart();
1467 auto range = e3->getParenOrBraceRange();
1468 if (loc1.isFileID() && range.getBegin().isFileID()
1469 && range.getEnd().isFileID())
1471 auto loc2 = range.getBegin();
1472 for (bool first = true;; first = false) {
1473 unsigned n = Lexer::MeasureTokenLength(
1474 loc2, compiler.getSourceManager(), compiler.getLangOpts());
1475 if (!first) {
1476 StringRef s(
1477 compiler.getSourceManager().getCharacterData(loc2), n);
1478 while (s.startswith("\\\n")) {
1479 s = s.drop_front(2);
1480 while (!s.empty()
1481 && (s.front() == ' ' || s.front() == '\t'
1482 || s.front() == '\n' || s.front() == '\v'
1483 || s.front() == '\f'))
1485 s = s.drop_front(1);
1488 if (!(s.empty() || s.startswith("/*") || s.startswith("//")
1489 || s == "\\"))
1491 break;
1494 loc2 = loc2.getLocWithOffset(std::max<unsigned>(n, 1));
1496 auto loc3 = range.getEnd();
1497 for (;;) {
1498 auto l = Lexer::GetBeginningOfToken(
1499 loc3.getLocWithOffset(-1), compiler.getSourceManager(),
1500 compiler.getLangOpts());
1501 unsigned n = Lexer::MeasureTokenLength(
1502 l, compiler.getSourceManager(), compiler.getLangOpts());
1503 StringRef s(compiler.getSourceManager().getCharacterData(l), n);
1504 while (s.startswith("\\\n")) {
1505 s = s.drop_front(2);
1506 while (!s.empty()
1507 && (s.front() == ' ' || s.front() == '\t'
1508 || s.front() == '\n' || s.front() == '\v'
1509 || s.front() == '\f'))
1511 s = s.drop_front(1);
1514 if (!(s.empty() || s.startswith("/*") || s.startswith("//")
1515 || s == "\\"))
1517 break;
1519 loc3 = l;
1521 if (removeText(CharSourceRange(SourceRange(loc1, loc2), false))) {
1522 if (removeText(SourceRange(loc3, range.getEnd()))) {
1523 return;
1525 report(DiagnosticsEngine::Fatal, "Corrupt rewrite", loc3)
1526 << expr->getSourceRange();
1527 return;
1531 report(
1532 DiagnosticsEngine::Warning,
1533 ("in call of %0, replace OUString constructed from a string literal"
1534 " directly with the string literal"),
1535 e3->getExprLoc())
1536 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1539 loplugin::Plugin::Registration< StringConstant > X("stringconstant", true);
1543 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */