bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / stringconstant.cxx
blob2360ef2e36209f58db822d4e7a3c9804f4439815
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 <cstdint>
13 #include <cstdlib>
14 #include <iomanip>
15 #include <limits>
16 #include <sstream>
17 #include <stack>
18 #include <string>
19 #include <vector>
20 #include <iostream>
22 #include "check.hxx"
23 #include "compat.hxx"
24 #include "plugin.hxx"
26 // Define a "string constant" to be a constant expression either of type "array
27 // of N char" where each array element is a non-NULL ASCII character---except
28 // that the last array element may be NULL, or, in some situations, of type char
29 // with an ASCII value (including NULL). Note that the former includes
30 // expressions denoting narrow string literals like "foo", and, with toolchains
31 // that support constexpr, constexpr variables declared like
33 // constexpr char str[] = "bar";
35 // This plugin flags uses of OUString functions with string constant arguments
36 // that can be rewritten more directly, like
38 // OUString::createFromAscii("foo") -> "foo"
40 // and
42 // s.equals(OUString("bar")) -> s == "bar"
44 namespace {
46 SourceLocation getMemberLocation(Expr const * expr) {
47 CallExpr const * e1 = dyn_cast<CallExpr>(expr);
48 MemberExpr const * e2 = e1 == nullptr
49 ? nullptr : dyn_cast<MemberExpr>(e1->getCallee());
50 return e2 == nullptr ? expr->getExprLoc()/*TODO*/ : e2->getMemberLoc();
53 bool isLhsOfAssignment(FunctionDecl const * decl, unsigned parameter) {
54 if (parameter != 0) {
55 return false;
57 auto oo = decl->getOverloadedOperator();
58 return oo == OO_Equal
59 || (oo >= OO_PlusEqual && oo <= OO_GreaterGreaterEqual);
62 bool hasOverloads(FunctionDecl const * decl, unsigned arguments) {
63 int n = 0;
64 auto ctx = decl->getDeclContext();
65 if (ctx->getDeclKind() == Decl::LinkageSpec) {
66 ctx = ctx->getParent();
68 auto res = ctx->lookup(decl->getDeclName());
69 for (auto d = res.begin(); d != res.end(); ++d) {
70 FunctionDecl const * f = dyn_cast<FunctionDecl>(*d);
71 if (f != nullptr && f->getMinRequiredArguments() <= arguments
72 && f->getNumParams() >= arguments)
74 auto consDecl = dyn_cast<CXXConstructorDecl>(f);
75 if (consDecl && consDecl->isCopyOrMoveConstructor()) {
76 continue;
78 ++n;
79 if (n == 2) {
80 return true;
84 return false;
87 CXXConstructExpr const * lookForCXXConstructExpr(Expr const * expr) {
88 if (auto e = dyn_cast<MaterializeTemporaryExpr>(expr)) {
89 expr = e->GetTemporaryExpr();
91 if (auto e = dyn_cast<CXXFunctionalCastExpr>(expr)) {
92 expr = e->getSubExpr();
94 if (auto e = dyn_cast<CXXBindTemporaryExpr>(expr)) {
95 expr = e->getSubExpr();
97 return dyn_cast<CXXConstructExpr>(expr);
100 char const * adviseNonArray(bool nonArray) {
101 return nonArray
102 ? ", and turn the non-array string constant into an array" : "";
105 class StringConstant:
106 public loplugin::FilteringRewritePlugin<StringConstant>
108 public:
109 explicit StringConstant(loplugin::InstantiationData const & data):
110 FilteringRewritePlugin(data) {}
112 void run() override;
114 bool TraverseFunctionDecl(FunctionDecl * decl) {
115 returnTypes_.push(compat::getDeclaredReturnType(decl));
116 auto const ret = RecursiveASTVisitor::TraverseFunctionDecl(decl);
117 assert(!returnTypes_.empty());
118 assert(returnTypes_.top() == compat::getDeclaredReturnType(decl));
119 returnTypes_.pop();
120 return ret;
123 bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl * decl) {
124 returnTypes_.push(compat::getDeclaredReturnType(decl));
125 auto const ret = RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(
126 decl);
127 assert(!returnTypes_.empty());
128 assert(returnTypes_.top() == compat::getDeclaredReturnType(decl));
129 returnTypes_.pop();
130 return ret;
133 bool TraverseCXXMethodDecl(CXXMethodDecl * decl) {
134 returnTypes_.push(compat::getDeclaredReturnType(decl));
135 auto const ret = RecursiveASTVisitor::TraverseCXXMethodDecl(decl);
136 assert(!returnTypes_.empty());
137 assert(returnTypes_.top() == compat::getDeclaredReturnType(decl));
138 returnTypes_.pop();
139 return ret;
142 bool TraverseCXXConstructorDecl(CXXConstructorDecl * decl) {
143 returnTypes_.push(compat::getDeclaredReturnType(decl));
144 auto const ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(decl);
145 assert(!returnTypes_.empty());
146 assert(returnTypes_.top() == compat::getDeclaredReturnType(decl));
147 returnTypes_.pop();
148 return ret;
151 bool TraverseCXXDestructorDecl(CXXDestructorDecl * decl) {
152 returnTypes_.push(compat::getDeclaredReturnType(decl));
153 auto const ret = RecursiveASTVisitor::TraverseCXXDestructorDecl(decl);
154 assert(!returnTypes_.empty());
155 assert(returnTypes_.top() == compat::getDeclaredReturnType(decl));
156 returnTypes_.pop();
157 return ret;
160 bool TraverseCXXConversionDecl(CXXConversionDecl * decl) {
161 returnTypes_.push(compat::getDeclaredReturnType(decl));
162 auto const ret = RecursiveASTVisitor::TraverseCXXConversionDecl(decl);
163 assert(!returnTypes_.empty());
164 assert(returnTypes_.top() == compat::getDeclaredReturnType(decl));
165 returnTypes_.pop();
166 return ret;
169 bool TraverseObjCMethodDecl(ObjCMethodDecl * decl) {
170 returnTypes_.push(decl->getReturnType());
171 auto const ret = RecursiveASTVisitor::TraverseObjCMethodDecl(decl);
172 assert(!returnTypes_.empty());
173 assert(returnTypes_.top() == decl->getReturnType());
174 returnTypes_.pop();
175 return ret;
178 bool TraverseCallExpr(CallExpr * expr);
180 bool TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr);
182 bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr * expr);
184 bool TraverseCXXConstructExpr(CXXConstructExpr * expr);
186 bool VisitCallExpr(CallExpr const * expr);
188 bool VisitCXXConstructExpr(CXXConstructExpr const * expr);
190 bool VisitReturnStmt(ReturnStmt const * stmt);
192 private:
193 enum class ContentKind { Ascii, Utf8, Arbitrary };
195 enum class TreatEmpty { DefaultCtor, CheckEmpty, Error };
197 enum class ChangeKind { Char, CharLen, SingleChar, OUStringChar };
199 enum class PassThrough { No, EmptyConstantString, NonEmptyConstantString };
201 std::string describeChangeKind(ChangeKind kind);
203 bool isStringConstant(
204 Expr const * expr, unsigned * size, bool * nonArray,
205 ContentKind * content, bool * embeddedNuls, bool * terminatingNul,
206 std::vector<char32_t> * utf8Content = nullptr);
208 bool isZero(Expr const * expr);
210 void reportChange(
211 Expr const * expr, ChangeKind kind, std::string const & original,
212 std::string const & replacement, PassThrough pass, bool nonArray,
213 char const * rewriteFrom, char const * rewriteTo);
215 void checkEmpty(
216 CallExpr const * expr, FunctionDecl const * callee,
217 TreatEmpty treatEmpty, unsigned size, std::string * replacement);
219 void handleChar(
220 CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
221 std::string const & replacement, TreatEmpty treatEmpty, bool literal,
222 char const * rewriteFrom = nullptr, char const * rewriteTo = nullptr);
224 void handleCharLen(
225 CallExpr const * expr, unsigned arg1, unsigned arg2,
226 FunctionDecl const * callee, std::string const & replacement,
227 TreatEmpty treatEmpty);
229 void handleOUStringCtor(
230 CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
231 bool explicitFunctionalCastNotation);
233 void handleOStringCtor(
234 CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
235 bool explicitFunctionalCastNotation);
237 void handleOUStringCtor(
238 Expr const * expr, Expr const * argExpr, FunctionDecl const * callee,
239 bool explicitFunctionalCastNotation);
241 void handleOStringCtor(
242 Expr const * expr, Expr const * argExpr, FunctionDecl const * callee,
243 bool explicitFunctionalCastNotation);
245 enum class StringKind { Unicode, Char };
246 void handleStringCtor(
247 Expr const * expr, Expr const * argExpr, FunctionDecl const * callee,
248 bool explicitFunctionalCastNotation, StringKind stringKind);
250 void handleFunArgOstring(
251 CallExpr const * expr, unsigned arg, FunctionDecl const * callee);
253 std::stack<QualType> returnTypes_;
254 std::stack<Expr const *> calls_;
257 void StringConstant::run() {
258 if (compiler.getLangOpts().CPlusPlus
259 && compiler.getPreprocessor().getIdentifierInfo(
260 "LIBO_INTERNAL_ONLY")->hasMacroDefinition())
261 //TODO: some parts of it are useful for external code, too
263 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
267 bool StringConstant::TraverseCallExpr(CallExpr * expr) {
268 if (!WalkUpFromCallExpr(expr)) {
269 return false;
271 calls_.push(expr);
272 bool bRes = true;
273 for (auto * e: expr->children()) {
274 if (!TraverseStmt(e)) {
275 bRes = false;
276 break;
279 calls_.pop();
280 return bRes;
283 bool StringConstant::TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr) {
284 if (!WalkUpFromCXXMemberCallExpr(expr)) {
285 return false;
287 calls_.push(expr);
288 bool bRes = true;
289 for (auto * e: expr->children()) {
290 if (!TraverseStmt(e)) {
291 bRes = false;
292 break;
295 calls_.pop();
296 return bRes;
299 bool StringConstant::TraverseCXXOperatorCallExpr(CXXOperatorCallExpr * expr)
301 if (!WalkUpFromCXXOperatorCallExpr(expr)) {
302 return false;
304 calls_.push(expr);
305 bool bRes = true;
306 for (auto * e: expr->children()) {
307 if (!TraverseStmt(e)) {
308 bRes = false;
309 break;
312 calls_.pop();
313 return bRes;
316 bool StringConstant::TraverseCXXConstructExpr(CXXConstructExpr * expr) {
317 if (!WalkUpFromCXXConstructExpr(expr)) {
318 return false;
320 calls_.push(expr);
321 bool bRes = true;
322 for (auto * e: expr->children()) {
323 if (!TraverseStmt(e)) {
324 bRes = false;
325 break;
328 calls_.pop();
329 return bRes;
332 bool StringConstant::VisitCallExpr(CallExpr const * expr) {
333 if (ignoreLocation(expr)) {
334 return true;
336 FunctionDecl const * fdecl = expr->getDirectCallee();
337 if (fdecl == nullptr) {
338 return true;
340 for (unsigned i = 0; i != fdecl->getNumParams(); ++i) {
341 auto t = fdecl->getParamDecl(i)->getType();
342 if (loplugin::TypeCheck(t).NotSubstTemplateTypeParmType()
343 .LvalueReference().Const().NotSubstTemplateTypeParmType()
344 .Class("OUString").Namespace("rtl").GlobalNamespace())
346 if (!(isLhsOfAssignment(fdecl, i)
347 || hasOverloads(fdecl, expr->getNumArgs())))
349 handleOUStringCtor(expr, i, fdecl, true);
352 if (loplugin::TypeCheck(t).NotSubstTemplateTypeParmType()
353 .LvalueReference().Const().NotSubstTemplateTypeParmType()
354 .Class("OString").Namespace("rtl").GlobalNamespace())
356 if (!(isLhsOfAssignment(fdecl, i)
357 || hasOverloads(fdecl, expr->getNumArgs())))
359 handleOStringCtor(expr, i, fdecl, true);
363 loplugin::DeclCheck dc(fdecl);
364 //TODO: u.compareToAscii("foo") -> u.???("foo")
365 //TODO: u.compareToIgnoreAsciiCaseAscii("foo") -> u.???("foo")
366 if ((dc.Function("createFromAscii").Class("OUString").Namespace("rtl")
367 .GlobalNamespace())
368 && fdecl->getNumParams() == 1)
370 // OUString::createFromAscii("foo") -> OUString("foo")
371 handleChar(
372 expr, 0, fdecl, "rtl::OUString constructor",
373 TreatEmpty::DefaultCtor, true);
374 return true;
376 if ((dc.Function("endsWithAsciiL").Class("OUString").Namespace("rtl")
377 .GlobalNamespace())
378 && fdecl->getNumParams() == 2)
380 // u.endsWithAsciiL("foo", 3) -> u.endsWith("foo"):
381 handleCharLen(
382 expr, 0, 1, fdecl, "rtl::OUString::endsWith", TreatEmpty::Error);
383 return true;
385 if ((dc.Function("endsWithIgnoreAsciiCaseAsciiL").Class("OUString")
386 .Namespace("rtl").GlobalNamespace())
387 && fdecl->getNumParams() == 2)
389 // u.endsWithIgnoreAsciiCaseAsciiL("foo", 3) ->
390 // u.endsWithIgnoreAsciiCase("foo"):
391 handleCharLen(
392 expr, 0, 1, fdecl, "rtl::OUString::endsWithIgnoreAsciiCase",
393 TreatEmpty::Error);
394 return true;
396 if ((dc.Function("equalsAscii").Class("OUString").Namespace("rtl")
397 .GlobalNamespace())
398 && fdecl->getNumParams() == 1)
400 // u.equalsAscii("foo") -> u == "foo":
401 handleChar(
402 expr, 0, fdecl, "operator ==", TreatEmpty::CheckEmpty, false);
403 return true;
405 if ((dc.Function("equalsAsciiL").Class("OUString").Namespace("rtl")
406 .GlobalNamespace())
407 && fdecl->getNumParams() == 2)
409 // u.equalsAsciiL("foo", 3) -> u == "foo":
410 handleCharLen(expr, 0, 1, fdecl, "operator ==", TreatEmpty::CheckEmpty);
411 return true;
413 if ((dc.Function("equalsIgnoreAsciiCaseAscii").Class("OUString")
414 .Namespace("rtl").GlobalNamespace())
415 && fdecl->getNumParams() == 1)
417 // u.equalsIgnoreAsciiCaseAscii("foo") ->
418 // u.equalsIngoreAsciiCase("foo"):
420 auto file = getFilenameOfLocation(
421 compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr)));
422 if (loplugin::isSamePathname(
423 file, SRCDIR "/sal/qa/rtl/strings/test_oustring_compare.cxx"))
425 return true;
427 handleChar(
428 expr, 0, fdecl, "rtl::OUString::equalsIgnoreAsciiCase",
429 TreatEmpty::CheckEmpty, false);
430 return true;
432 if ((dc.Function("equalsIgnoreAsciiCaseAsciiL").Class("OUString")
433 .Namespace("rtl").GlobalNamespace())
434 && fdecl->getNumParams() == 2)
436 // u.equalsIgnoreAsciiCaseAsciiL("foo", 3) ->
437 // u.equalsIngoreAsciiCase("foo"):
438 auto file = getFilenameOfLocation(
439 compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr)));
440 if (loplugin::isSamePathname(
441 file, SRCDIR "/sal/qa/rtl/strings/test_oustring_compare.cxx"))
443 return true;
445 handleCharLen(
446 expr, 0, 1, fdecl, "rtl::OUString::equalsIgnoreAsciiCase",
447 TreatEmpty::CheckEmpty);
448 return true;
450 if ((dc.Function("indexOfAsciiL").Class("OUString").Namespace("rtl")
451 .GlobalNamespace())
452 && fdecl->getNumParams() == 3)
454 assert(expr->getNumArgs() == 3);
455 // u.indexOfAsciiL("foo", 3, i) -> u.indexOf("foo", i):
456 handleCharLen(
457 expr, 0, 1, fdecl, "rtl::OUString::indexOf", TreatEmpty::Error);
458 return true;
460 if ((dc.Function("lastIndexOfAsciiL").Class("OUString").Namespace("rtl")
461 .GlobalNamespace())
462 && fdecl->getNumParams() == 2)
464 // u.lastIndexOfAsciiL("foo", 3) -> u.lastIndexOf("foo"):
465 handleCharLen(
466 expr, 0, 1, fdecl, "rtl::OUString::lastIndexOf", TreatEmpty::Error);
467 return true;
469 if ((dc.Function("matchAsciiL").Class("OUString").Namespace("rtl")
470 .GlobalNamespace())
471 && fdecl->getNumParams() == 3)
473 assert(expr->getNumArgs() == 3);
474 // u.matchAsciiL("foo", 3, i) -> u.match("foo", i):
475 handleCharLen(
476 expr, 0, 1, fdecl,
477 (isZero(expr->getArg(2))
478 ? std::string("rtl::OUString::startsWith")
479 : std::string("rtl::OUString::match")),
480 TreatEmpty::Error);
481 return true;
483 if ((dc.Function("matchIgnoreAsciiCaseAsciiL").Class("OUString")
484 .Namespace("rtl").GlobalNamespace())
485 && fdecl->getNumParams() == 3)
487 assert(expr->getNumArgs() == 3);
488 // u.matchIgnoreAsciiCaseAsciiL("foo", 3, i) ->
489 // u.matchIgnoreAsciiCase("foo", i):
490 handleCharLen(
491 expr, 0, 1, fdecl,
492 (isZero(expr->getArg(2))
493 ? std::string("rtl::OUString::startsWithIgnoreAsciiCase")
494 : std::string("rtl::OUString::matchIgnoreAsciiCase")),
495 TreatEmpty::Error);
496 return true;
498 if ((dc.Function("reverseCompareToAsciiL").Class("OUString")
499 .Namespace("rtl").GlobalNamespace())
500 && fdecl->getNumParams() == 2)
502 // u.reverseCompareToAsciiL("foo", 3) -> u.reverseCompareTo("foo"):
503 handleCharLen(
504 expr, 0, 1, fdecl, "rtl::OUString::reverseCompareTo",
505 TreatEmpty::Error);
506 return true;
508 if ((dc.Function("reverseCompareTo").Class("OUString").Namespace("rtl")
509 .GlobalNamespace())
510 && fdecl->getNumParams() == 1)
512 handleOUStringCtor(expr, 0, fdecl, false);
513 return true;
515 if ((dc.Function("equalsIgnoreAsciiCase").Class("OUString").Namespace("rtl")
516 .GlobalNamespace())
517 && fdecl->getNumParams() == 1)
519 handleOUStringCtor(expr, 0, fdecl, false);
520 return true;
522 if ((dc.Function("match").Class("OUString").Namespace("rtl")
523 .GlobalNamespace())
524 && fdecl->getNumParams() == 2)
526 handleOUStringCtor(expr, 0, fdecl, false);
527 return true;
529 if ((dc.Function("matchIgnoreAsciiCase").Class("OUString").Namespace("rtl")
530 .GlobalNamespace())
531 && fdecl->getNumParams() == 2)
533 handleOUStringCtor(expr, 0, fdecl, false);
534 return true;
536 if ((dc.Function("startsWith").Class("OUString").Namespace("rtl")
537 .GlobalNamespace())
538 && fdecl->getNumParams() == 2)
540 handleOUStringCtor(expr, 0, fdecl, false);
541 return true;
543 if ((dc.Function("startsWithIgnoreAsciiCase").Class("OUString")
544 .Namespace("rtl").GlobalNamespace())
545 && fdecl->getNumParams() == 2)
547 handleOUStringCtor(expr, 0, fdecl, false);
548 return true;
550 if ((dc.Function("endsWith").Class("OUString").Namespace("rtl")
551 .GlobalNamespace())
552 && fdecl->getNumParams() == 2)
554 handleOUStringCtor(expr, 0, fdecl, false);
555 return true;
557 if ((dc.Function("endsWithIgnoreAsciiCase").Class("OUString")
558 .Namespace("rtl").GlobalNamespace())
559 && fdecl->getNumParams() == 2)
561 handleOUStringCtor(expr, 0, fdecl, false);
562 return true;
564 if ((dc.Function("indexOf").Class("OUString").Namespace("rtl")
565 .GlobalNamespace())
566 && fdecl->getNumParams() == 2)
568 handleOUStringCtor(expr, 0, fdecl, false);
569 return true;
571 if ((dc.Function("lastIndexOf").Class("OUString").Namespace("rtl")
572 .GlobalNamespace())
573 && fdecl->getNumParams() == 1)
575 handleOUStringCtor(expr, 0, fdecl, false);
576 return true;
578 if ((dc.Function("replaceFirst").Class("OUString").Namespace("rtl")
579 .GlobalNamespace())
580 && fdecl->getNumParams() == 3)
582 handleOUStringCtor(expr, 0, fdecl, false);
583 handleOUStringCtor(expr, 1, fdecl, false);
584 return true;
586 if ((dc.Function("replaceAll").Class("OUString").Namespace("rtl")
587 .GlobalNamespace())
588 && (fdecl->getNumParams() == 2 || fdecl->getNumParams() == 3))
590 handleOUStringCtor(expr, 0, fdecl, false);
591 handleOUStringCtor(expr, 1, fdecl, false);
592 return true;
594 if ((dc.Operator(OO_PlusEqual).Class("OUString").Namespace("rtl")
595 .GlobalNamespace())
596 && fdecl->getNumParams() == 1)
598 handleOUStringCtor(
599 expr, dyn_cast<CXXOperatorCallExpr>(expr) == nullptr ? 0 : 1,
600 fdecl, false);
601 return true;
603 if ((dc.Function("equals").Class("OUString").Namespace("rtl")
604 .GlobalNamespace())
605 && fdecl->getNumParams() == 1)
607 unsigned n;
608 bool nonArray;
609 ContentKind cont;
610 bool emb;
611 bool trm;
612 if (!isStringConstant(
613 expr->getArg(0)->IgnoreParenImpCasts(), &n, &nonArray, &cont,
614 &emb, &trm))
616 return true;
618 if (cont != ContentKind::Ascii) {
619 report(
620 DiagnosticsEngine::Warning,
621 ("call of '%0' with string constant argument containing"
622 " non-ASCII characters"),
623 expr->getExprLoc())
624 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
626 if (emb) {
627 report(
628 DiagnosticsEngine::Warning,
629 ("call of '%0' with string constant argument containing"
630 " embedded NULLs"),
631 expr->getExprLoc())
632 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
634 if (n == 0) {
635 report(
636 DiagnosticsEngine::Warning,
637 ("rewrite call of '%0' with empty string constant argument as"
638 " call of 'rtl::OUString::isEmpty'"),
639 expr->getExprLoc())
640 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
641 return true;
644 if (dc.Operator(OO_EqualEqual).Namespace("rtl").GlobalNamespace()
645 && fdecl->getNumParams() == 2)
647 for (unsigned i = 0; i != 2; ++i) {
648 unsigned n;
649 bool nonArray;
650 ContentKind cont;
651 bool emb;
652 bool trm;
653 if (!isStringConstant(
654 expr->getArg(i)->IgnoreParenImpCasts(), &n, &nonArray,
655 &cont, &emb, &trm))
657 continue;
659 if (cont != ContentKind::Ascii) {
660 report(
661 DiagnosticsEngine::Warning,
662 ("call of '%0' with string constant argument containing"
663 " non-ASCII characters"),
664 expr->getExprLoc())
665 << fdecl->getQualifiedNameAsString()
666 << expr->getSourceRange();
668 if (emb) {
669 report(
670 DiagnosticsEngine::Warning,
671 ("call of '%0' with string constant argument containing"
672 " embedded NULLs"),
673 expr->getExprLoc())
674 << fdecl->getQualifiedNameAsString()
675 << expr->getSourceRange();
677 if (n == 0) {
678 report(
679 DiagnosticsEngine::Warning,
680 ("rewrite call of '%0' with empty string constant argument"
681 " as call of 'rtl::OUString::isEmpty'"),
682 expr->getExprLoc())
683 << fdecl->getQualifiedNameAsString()
684 << expr->getSourceRange();
687 return true;
689 if (dc.Operator(OO_ExclaimEqual).Namespace("rtl").GlobalNamespace()
690 && fdecl->getNumParams() == 2)
692 for (unsigned i = 0; i != 2; ++i) {
693 unsigned n;
694 bool nonArray;
695 ContentKind cont;
696 bool emb;
697 bool trm;
698 if (!isStringConstant(
699 expr->getArg(i)->IgnoreParenImpCasts(), &n, &nonArray,
700 &cont, &emb, &trm))
702 continue;
704 if (cont != ContentKind::Ascii) {
705 report(
706 DiagnosticsEngine::Warning,
707 ("call of '%0' with string constant argument containing"
708 " non-ASCII characters"),
709 expr->getExprLoc())
710 << fdecl->getQualifiedNameAsString()
711 << expr->getSourceRange();
713 if (emb) {
714 report(
715 DiagnosticsEngine::Warning,
716 ("call of '%0' with string constant argument containing"
717 " embedded NULLs"),
718 expr->getExprLoc())
719 << fdecl->getQualifiedNameAsString()
720 << expr->getSourceRange();
722 if (n == 0) {
723 report(
724 DiagnosticsEngine::Warning,
725 ("rewrite call of '%0' with empty string constant argument"
726 " as call of '!rtl::OUString::isEmpty'"),
727 expr->getExprLoc())
728 << fdecl->getQualifiedNameAsString()
729 << expr->getSourceRange();
732 return true;
734 if (dc.Operator(OO_Equal).Namespace("rtl").GlobalNamespace()
735 && fdecl->getNumParams() == 1)
737 unsigned n;
738 bool nonArray;
739 ContentKind cont;
740 bool emb;
741 bool trm;
742 if (!isStringConstant(
743 expr->getArg(1)->IgnoreParenImpCasts(), &n, &nonArray, &cont,
744 &emb, &trm))
746 return true;
748 if (cont != ContentKind::Ascii) {
749 report(
750 DiagnosticsEngine::Warning,
751 ("call of '%0' with string constant argument containing"
752 " non-ASCII characters"),
753 expr->getExprLoc())
754 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
756 if (emb) {
757 report(
758 DiagnosticsEngine::Warning,
759 ("call of '%0' with string constant argument containing"
760 " embedded NULLs"),
761 expr->getExprLoc())
762 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
764 if (n == 0) {
765 report(
766 DiagnosticsEngine::Warning,
767 ("rewrite call of '%0' with empty string constant argument as"
768 " call of 'rtl::OUString::clear'"),
769 expr->getExprLoc())
770 << fdecl->getQualifiedNameAsString() << expr->getSourceRange();
771 return true;
773 return true;
775 if (dc.Function("append").Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
776 && fdecl->getNumParams() == 1)
778 handleChar(expr, 0, fdecl, "", TreatEmpty::Error, false);
779 return true;
781 if ((dc.Function("appendAscii").Class("OUStringBuffer").Namespace("rtl")
782 .GlobalNamespace())
783 && fdecl->getNumParams() == 1)
785 // u.appendAscii("foo") -> u.append("foo")
786 handleChar(
787 expr, 0, fdecl, "rtl::OUStringBuffer::append", TreatEmpty::Error,
788 true, "appendAscii", "append");
789 return true;
791 if ((dc.Function("appendAscii").Class("OUStringBuffer").Namespace("rtl")
792 .GlobalNamespace())
793 && fdecl->getNumParams() == 2)
795 // u.appendAscii("foo", 3) -> u.append("foo"):
796 handleCharLen(
797 expr, 0, 1, fdecl, "rtl::OUStringBuffer::append",
798 TreatEmpty::Error);
799 return true;
801 if (dc.Function("append").Class("OStringBuffer").Namespace("rtl")
802 .GlobalNamespace())
804 switch (fdecl->getNumParams()) {
805 case 1:
806 handleFunArgOstring(expr, 0, fdecl);
807 break;
808 case 2:
810 // b.append("foo", 3) -> b.append("foo"):
811 auto file = getFilenameOfLocation(
812 compiler.getSourceManager().getSpellingLoc(
813 compat::getBeginLoc(expr)));
814 if (loplugin::isSamePathname(
815 file,
816 SRCDIR "/sal/qa/OStringBuffer/rtl_OStringBuffer.cxx"))
818 return true;
820 handleCharLen(
821 expr, 0, 1, fdecl, "rtl::OStringBuffer::append",
822 TreatEmpty::Error);
824 break;
825 default:
826 break;
828 return true;
830 if (dc.Function("insert").Class("OStringBuffer").Namespace("rtl")
831 .GlobalNamespace())
833 switch (fdecl->getNumParams()) {
834 case 2:
835 handleFunArgOstring(expr, 1, fdecl);
836 break;
837 case 3:
839 // b.insert(i, "foo", 3) -> b.insert(i, "foo"):
840 handleCharLen(
841 expr, 1, 2, fdecl, "rtl::OStringBuffer::insert",
842 TreatEmpty::Error);
843 break;
845 default:
846 break;
848 return true;
850 return true;
853 bool StringConstant::VisitCXXConstructExpr(CXXConstructExpr const * expr) {
854 if (ignoreLocation(expr)) {
855 return true;
857 auto classdecl = expr->getConstructor()->getParent();
858 if (loplugin::DeclCheck(classdecl)
859 .Class("OUString").Namespace("rtl").GlobalNamespace())
861 ChangeKind kind;
862 PassThrough pass;
863 bool simplify;
864 switch (expr->getConstructor()->getNumParams()) {
865 case 1:
866 if (!loplugin::TypeCheck(
867 expr->getConstructor()->getParamDecl(0)->getType())
868 .Typedef("sal_Unicode").GlobalNamespace())
870 return true;
872 kind = ChangeKind::SingleChar;
873 pass = PassThrough::NonEmptyConstantString;
874 simplify = false;
875 break;
876 case 2:
878 auto arg = expr->getArg(0);
879 if (loplugin::TypeCheck(arg->getType())
880 .Class("OUStringChar_").Namespace("rtl")
881 .GlobalNamespace())
883 kind = ChangeKind::OUStringChar;
884 pass = PassThrough::NonEmptyConstantString;
885 simplify = false;
886 } else {
887 unsigned n;
888 bool nonArray;
889 ContentKind cont;
890 bool emb;
891 bool trm;
892 if (!isStringConstant(
893 arg->IgnoreParenImpCasts(), &n, &nonArray, &cont,
894 &emb, &trm))
896 return true;
898 // OSL_THIS_FUNC may be defined as "" or as something other
899 // than a string literal in include/osl/diagnose.h:
900 auto loc = compat::getBeginLoc(arg);
901 if (compiler.getSourceManager().isMacroBodyExpansion(loc)
902 && (Lexer::getImmediateMacroName(
903 loc, compiler.getSourceManager(),
904 compiler.getLangOpts())
905 == "OSL_THIS_FUNC"))
907 return true;
909 if (cont != ContentKind::Ascii) {
910 report(
911 DiagnosticsEngine::Warning,
912 ("construction of %0 with string constant argument"
913 " containing non-ASCII characters"),
914 expr->getExprLoc())
915 << classdecl << expr->getSourceRange();
917 if (emb) {
918 report(
919 DiagnosticsEngine::Warning,
920 ("construction of %0 with string constant argument"
921 " containing embedded NULLs"),
922 expr->getExprLoc())
923 << classdecl << expr->getSourceRange();
925 kind = ChangeKind::Char;
926 pass = n == 0
927 ? PassThrough::EmptyConstantString
928 : PassThrough::NonEmptyConstantString;
929 simplify = false;
931 break;
933 case 4:
935 unsigned n;
936 bool nonArray;
937 ContentKind cont;
938 bool emb;
939 bool trm;
940 std::vector<char32_t> utf8Cont;
941 if (!isStringConstant(
942 expr->getArg(0)->IgnoreParenImpCasts(), &n, &nonArray,
943 &cont, &emb, &trm, &utf8Cont))
945 return true;
947 APSInt res;
948 if (!compat::EvaluateAsInt(expr->getArg(1),
949 res, compiler.getASTContext()))
951 return true;
953 if (res != n) {
954 report(
955 DiagnosticsEngine::Warning,
956 ("suspicious 'rtl::OUString' constructor with literal"
957 " of length %0 and non-matching length argument %1"),
958 expr->getExprLoc())
959 << n << res.toString(10) << expr->getSourceRange();
960 return true;
962 APSInt enc;
963 if (!compat::EvaluateAsInt(expr->getArg(2),
964 enc, compiler.getASTContext()))
966 return true;
968 auto const encIsAscii = enc == 11; // RTL_TEXTENCODING_ASCII_US
969 auto const encIsUtf8 = enc == 76; // RTL_TEXTENCODING_UTF8
970 if (!compat::EvaluateAsInt(expr->getArg(3),
971 res, compiler.getASTContext())
972 || res != 0x333) // OSTRING_TO_OUSTRING_CVTFLAGS
974 return true;
976 if (!encIsAscii && cont == ContentKind::Ascii) {
977 report(
978 DiagnosticsEngine::Warning,
979 ("suspicious 'rtl::OUString' constructor with text"
980 " encoding %0 but plain ASCII content; use"
981 " 'RTL_TEXTENCODING_ASCII_US' instead"),
982 expr->getArg(2)->getExprLoc())
983 << enc.toString(10) << expr->getSourceRange();
984 return true;
986 if (encIsUtf8) {
987 if (cont == ContentKind::Arbitrary) {
988 report(
989 DiagnosticsEngine::Warning,
990 ("suspicious 'rtl::OUString' constructor with text"
991 " encoding 'RTL_TEXTENCODING_UTF8' but non-UTF-8"
992 " content"),
993 expr->getArg(0)->getExprLoc())
994 << expr->getSourceRange();
995 } else {
996 assert(cont == ContentKind::Utf8);
997 //TODO: keep original content as much as possible
998 std::ostringstream s;
999 for (auto const c: utf8Cont) {
1000 if (c == '\\') {
1001 s << "\\\\";
1002 } else if (c == '"') {
1003 s << "\\\"";
1004 } else if (c == '\a') {
1005 s << "\\a";
1006 } else if (c == '\b') {
1007 s << "\\b";
1008 } else if (c == '\f') {
1009 s << "\\f";
1010 } else if (c == '\n') {
1011 s << "\\n";
1012 } else if (c == '\r') {
1013 s << "\\r";
1014 } else if (c == '\t') {
1015 s << "\\r";
1016 } else if (c == '\v') {
1017 s << "\\v";
1018 } else if (c <= 0x1F || c == 0x7F) {
1019 s << "\\x" << std::oct << std::setw(3)
1020 << std::setfill('0')
1021 << static_cast<std::uint_least32_t>(c);
1022 } else if (c < 0x7F) {
1023 s << char(c);
1024 } else if (c <= 0xFFFF) {
1025 s << "\\u" << std::hex << std::uppercase
1026 << std::setw(4) << std::setfill('0')
1027 << static_cast<std::uint_least32_t>(c);
1028 } else {
1029 assert(c <= 0x10FFFF);
1030 s << "\\U" << std::hex << std::uppercase
1031 << std::setw(8) << std::setfill('0')
1032 << static_cast<std::uint_least32_t>(c);
1035 report(
1036 DiagnosticsEngine::Warning,
1037 ("simplify construction of %0 with UTF-8 content as"
1038 " OUString(u\"%1\")"),
1039 expr->getExprLoc())
1040 << classdecl << s.str() << expr->getSourceRange();
1043 return true;
1045 if (cont != ContentKind::Ascii || emb) {
1046 // cf. remaining uses of RTL_CONSTASCII_USTRINGPARAM
1047 return true;
1049 kind = ChangeKind::Char;
1050 pass = n == 0
1051 ? PassThrough::EmptyConstantString
1052 : PassThrough::NonEmptyConstantString;
1053 simplify = true;
1054 break;
1056 default:
1057 return true;
1059 if (!calls_.empty()) {
1060 Expr const * call = calls_.top();
1061 CallExpr::const_arg_iterator argsBeg;
1062 CallExpr::const_arg_iterator argsEnd;
1063 if (isa<CallExpr>(call)) {
1064 argsBeg = cast<CallExpr>(call)->arg_begin();
1065 argsEnd = cast<CallExpr>(call)->arg_end();
1066 } else if (isa<CXXConstructExpr>(call)) {
1067 argsBeg = cast<CXXConstructExpr>(call)->arg_begin();
1068 argsEnd = cast<CXXConstructExpr>(call)->arg_end();
1069 } else {
1070 assert(false);
1072 for (auto i(argsBeg); i != argsEnd; ++i) {
1073 Expr const * e = (*i)->IgnoreParenImpCasts();
1074 if (isa<MaterializeTemporaryExpr>(e)) {
1075 e = cast<MaterializeTemporaryExpr>(e)->GetTemporaryExpr()
1076 ->IgnoreParenImpCasts();
1078 if (isa<CXXFunctionalCastExpr>(e)) {
1079 e = cast<CXXFunctionalCastExpr>(e)->getSubExpr()
1080 ->IgnoreParenImpCasts();
1082 if (isa<CXXBindTemporaryExpr>(e)) {
1083 e = cast<CXXBindTemporaryExpr>(e)->getSubExpr()
1084 ->IgnoreParenImpCasts();
1086 if (e == expr) {
1087 if (isa<CallExpr>(call)) {
1088 FunctionDecl const * fdecl
1089 = cast<CallExpr>(call)->getDirectCallee();
1090 if (fdecl == nullptr) {
1091 break;
1093 loplugin::DeclCheck dc(fdecl);
1094 if (pass == PassThrough::EmptyConstantString) {
1095 if ((dc.Function("equals").Class("OUString")
1096 .Namespace("rtl").GlobalNamespace())
1097 || (dc.Operator(OO_EqualEqual).Namespace("rtl")
1098 .GlobalNamespace()))
1100 report(
1101 DiagnosticsEngine::Warning,
1102 ("rewrite call of '%0' with construction of"
1103 " %1 with empty string constant argument"
1104 " as call of 'rtl::OUString::isEmpty'"),
1105 getMemberLocation(call))
1106 << fdecl->getQualifiedNameAsString()
1107 << classdecl << call->getSourceRange();
1108 return true;
1110 if (dc.Operator(OO_ExclaimEqual).Namespace("rtl")
1111 .GlobalNamespace())
1113 report(
1114 DiagnosticsEngine::Warning,
1115 ("rewrite call of '%0' with construction of"
1116 " %1 with empty string constant argument"
1117 " as call of '!rtl::OUString::isEmpty'"),
1118 getMemberLocation(call))
1119 << fdecl->getQualifiedNameAsString()
1120 << classdecl << call->getSourceRange();
1121 return true;
1123 if ((dc.Operator(OO_Plus).Namespace("rtl")
1124 .GlobalNamespace())
1125 || (dc.Operator(OO_Plus).Class("OUString")
1126 .Namespace("rtl").GlobalNamespace()))
1128 report(
1129 DiagnosticsEngine::Warning,
1130 ("call of '%0' with suspicious construction"
1131 " of %1 with empty string constant"
1132 " argument"),
1133 getMemberLocation(call))
1134 << fdecl->getQualifiedNameAsString()
1135 << classdecl << call->getSourceRange();
1136 return true;
1138 if (dc.Operator(OO_Equal).Class("OUString")
1139 .Namespace("rtl").GlobalNamespace())
1141 report(
1142 DiagnosticsEngine::Warning,
1143 ("rewrite call of '%0' with construction of"
1144 " %1 with empty string constant argument"
1145 " as call of 'rtl::OUString::clear'"),
1146 getMemberLocation(call))
1147 << fdecl->getQualifiedNameAsString()
1148 << classdecl << call->getSourceRange();
1149 return true;
1151 } else {
1152 assert(pass == PassThrough::NonEmptyConstantString);
1153 if (dc.Function("equals").Class("OUString")
1154 .Namespace("rtl").GlobalNamespace())
1156 report(
1157 DiagnosticsEngine::Warning,
1158 ("rewrite call of '%0' with construction of"
1159 " %1 with %2 as 'operator =='"),
1160 getMemberLocation(call))
1161 << fdecl->getQualifiedNameAsString()
1162 << classdecl << describeChangeKind(kind)
1163 << call->getSourceRange();
1164 return true;
1166 if ((dc.Operator(OO_Plus).Namespace("rtl")
1167 .GlobalNamespace())
1168 || (dc.Operator(OO_Plus).Class("OUString")
1169 .Namespace("rtl").GlobalNamespace())
1170 || (dc.Operator(OO_EqualEqual).Namespace("rtl")
1171 .GlobalNamespace())
1172 || (dc.Operator(OO_ExclaimEqual)
1173 .Namespace("rtl").GlobalNamespace()))
1175 if (dc.Operator(OO_Plus).Namespace("rtl")
1176 .GlobalNamespace())
1178 auto file = getFilenameOfLocation(
1179 compiler.getSourceManager()
1180 .getSpellingLoc(
1181 compat::getBeginLoc(expr)));
1182 if (loplugin::isSamePathname(
1183 file,
1184 (SRCDIR
1185 "/sal/qa/rtl/strings/test_ostring_concat.cxx"))
1186 || loplugin::isSamePathname(
1187 file,
1188 (SRCDIR
1189 "/sal/qa/rtl/strings/test_oustring_concat.cxx")))
1191 return true;
1194 auto loc = compat::getBeginLoc(expr->getArg(0));
1195 while (compiler.getSourceManager()
1196 .isMacroArgExpansion(loc))
1198 loc = compiler.getSourceManager()
1199 .getImmediateMacroCallerLoc(loc);
1201 if ((compiler.getSourceManager()
1202 .isMacroBodyExpansion(loc))
1203 && (Lexer::getImmediateMacroName(
1204 loc, compiler.getSourceManager(),
1205 compiler.getLangOpts())
1206 == "OSL_THIS_FUNC"))
1208 return true;
1210 if (kind == ChangeKind::SingleChar) {
1211 report(
1212 DiagnosticsEngine::Warning,
1213 ("rewrite construction of %0 with %1 in"
1214 " call of '%2' as construction of"
1215 " 'OUStringChar'"),
1216 getMemberLocation(expr))
1217 << classdecl << describeChangeKind(kind)
1218 << fdecl->getQualifiedNameAsString()
1219 << expr->getSourceRange();
1220 } else {
1221 report(
1222 DiagnosticsEngine::Warning,
1223 ("elide construction of %0 with %1 in"
1224 " call of '%2'"),
1225 getMemberLocation(expr))
1226 << classdecl << describeChangeKind(kind)
1227 << fdecl->getQualifiedNameAsString()
1228 << expr->getSourceRange();
1230 return true;
1233 } else if (isa<CXXConstructExpr>(call)) {
1234 } else {
1235 assert(false);
1240 if (simplify) {
1241 report(
1242 DiagnosticsEngine::Warning,
1243 "simplify construction of %0 with %1", expr->getExprLoc())
1244 << classdecl << describeChangeKind(kind)
1245 << expr->getSourceRange();
1247 return true;
1250 auto consDecl = expr->getConstructor();
1251 for (unsigned i = 0; i != consDecl->getNumParams(); ++i) {
1252 auto t = consDecl->getParamDecl(i)->getType();
1253 if (loplugin::TypeCheck(t).NotSubstTemplateTypeParmType()
1254 .LvalueReference().Const().NotSubstTemplateTypeParmType()
1255 .Class("OUString").Namespace("rtl").GlobalNamespace())
1257 auto argExpr = expr->getArg(i);
1258 if (argExpr && i <= consDecl->getNumParams())
1260 if (!hasOverloads(consDecl, expr->getNumArgs()))
1262 handleOUStringCtor(expr, argExpr, consDecl, true);
1266 if (loplugin::TypeCheck(t).NotSubstTemplateTypeParmType()
1267 .LvalueReference().Const().NotSubstTemplateTypeParmType()
1268 .Class("OString").Namespace("rtl").GlobalNamespace())
1270 auto argExpr = expr->getArg(i);
1271 if (argExpr && i <= consDecl->getNumParams())
1273 if (!hasOverloads(consDecl, expr->getNumArgs()))
1275 handleOStringCtor(expr, argExpr, consDecl, true);
1281 return true;
1284 bool StringConstant::VisitReturnStmt(ReturnStmt const * stmt) {
1285 if (ignoreLocation(stmt)) {
1286 return true;
1288 auto const e1 = stmt->getRetValue();
1289 if (e1 == nullptr) {
1290 return true;
1292 auto const tc1 = loplugin::TypeCheck(e1->getType().getTypePtr());
1293 if (!(tc1.Class("OString").Namespace("rtl").GlobalNamespace()
1294 || tc1.Class("OUString").Namespace("rtl").GlobalNamespace()))
1296 return true;
1298 assert(!returnTypes_.empty());
1299 auto const tc2 = loplugin::TypeCheck(returnTypes_.top().getTypePtr());
1300 if (!(tc2.Class("OString").Namespace("rtl").GlobalNamespace()
1301 || tc2.Class("OUString").Namespace("rtl").GlobalNamespace()))
1303 return true;
1305 auto const e2 = dyn_cast<CXXFunctionalCastExpr>(e1->IgnoreImplicit());
1306 if (e2 == nullptr) {
1307 return true;
1309 auto const e3 = dyn_cast<CXXBindTemporaryExpr>(e2->getSubExpr());
1310 if (e3 == nullptr) {
1311 return true;
1313 auto const e4 = dyn_cast<CXXConstructExpr>(e3->getSubExpr());
1314 if (e4 == nullptr) {
1315 return true;
1317 if (e4->getNumArgs() != 2) {
1318 return true;
1320 auto const e5 = e4->getArg(1);
1321 if (!(isa<CXXDefaultArgExpr>(e5)
1322 && (loplugin::TypeCheck(e5->getType()).Struct("Dummy").Namespace("libreoffice_internal")
1323 .Namespace("rtl").GlobalNamespace())))
1325 return true;
1327 report(DiagnosticsEngine::Warning, "elide constructor call", compat::getBeginLoc(e1))
1328 << e1->getSourceRange();
1329 return true;
1332 std::string StringConstant::describeChangeKind(ChangeKind kind) {
1333 switch (kind) {
1334 case ChangeKind::Char:
1335 return "string constant argument";
1336 case ChangeKind::CharLen:
1337 return "string constant and matching length arguments";
1338 case ChangeKind::SingleChar:
1339 return "sal_Unicode argument";
1340 case ChangeKind::OUStringChar:
1341 return "OUStringChar argument";
1343 llvm_unreachable("unknown change kind");
1346 bool StringConstant::isStringConstant(
1347 Expr const * expr, unsigned * size, bool * nonArray, ContentKind * content,
1348 bool * embeddedNuls, bool * terminatingNul,
1349 std::vector<char32_t> * utf8Content)
1351 assert(expr != nullptr);
1352 assert(size != nullptr);
1353 assert(nonArray != nullptr);
1354 assert(content != nullptr);
1355 assert(embeddedNuls != nullptr);
1356 assert(terminatingNul != nullptr);
1357 QualType t = expr->getType();
1358 // Look inside RTL_CONSTASCII_STRINGPARAM:
1359 if (loplugin::TypeCheck(t).Pointer().Const().Char()) {
1360 auto e2 = dyn_cast<UnaryOperator>(expr);
1361 if (e2 != nullptr && e2->getOpcode() == UO_AddrOf) {
1362 auto e3 = dyn_cast<ArraySubscriptExpr>(
1363 e2->getSubExpr()->IgnoreParenImpCasts());
1364 if (e3 == nullptr || !isZero(e3->getIdx()->IgnoreParenImpCasts())) {
1365 return false;
1367 expr = e3->getBase()->IgnoreParenImpCasts();
1368 t = expr->getType();
1371 if (!t.isConstQualified()) {
1372 return false;
1374 DeclRefExpr const * dre = dyn_cast<DeclRefExpr>(expr);
1375 if (dre != nullptr) {
1376 VarDecl const * var = dyn_cast<VarDecl>(dre->getDecl());
1377 if (var != nullptr) {
1378 Expr const * init = var->getAnyInitializer();
1379 if (init != nullptr) {
1380 expr = init->IgnoreParenImpCasts();
1384 bool isPtr;
1385 if (loplugin::TypeCheck(t).Pointer().Const().Char()) {
1386 isPtr = true;
1387 } else if (t->isConstantArrayType()
1388 && (loplugin::TypeCheck(
1389 t->getAsArrayTypeUnsafe()->getElementType())
1390 .Char()))
1392 isPtr = false;
1393 } else {
1394 return false;
1396 clang::StringLiteral const * lit = dyn_cast<clang::StringLiteral>(expr);
1397 if (lit != nullptr) {
1398 if (!(lit->isAscii() || lit->isUTF8())) {
1399 return false;
1401 unsigned n = lit->getLength();
1402 ContentKind cont = ContentKind::Ascii;
1403 bool emb = false;
1404 char32_t val = 0;
1405 enum class Utf8State { Start, E0, EB, F0, F4, Trail1, Trail2, Trail3 };
1406 Utf8State s = Utf8State::Start;
1407 StringRef str = lit->getString();
1408 for (unsigned i = 0; i != n; ++i) {
1409 auto const c = static_cast<unsigned char>(str[i]);
1410 if (c == '\0') {
1411 emb = true;
1413 switch (s) {
1414 case Utf8State::Start:
1415 if (c >= 0x80) {
1416 if (c >= 0xC2 && c <= 0xDF) {
1417 val = c & 0x1F;
1418 s = Utf8State::Trail1;
1419 } else if (c == 0xE0) {
1420 val = c & 0x0F;
1421 s = Utf8State::E0;
1422 } else if ((c >= 0xE1 && c <= 0xEA)
1423 || (c >= 0xEE && c <= 0xEF))
1425 val = c & 0x0F;
1426 s = Utf8State::Trail2;
1427 } else if (c == 0xEB) {
1428 val = c & 0x0F;
1429 s = Utf8State::EB;
1430 } else if (c == 0xF0) {
1431 val = c & 0x03;
1432 s = Utf8State::F0;
1433 } else if (c >= 0xF1 && c <= 0xF3) {
1434 val = c & 0x03;
1435 s = Utf8State::Trail3;
1436 } else if (c == 0xF4) {
1437 val = c & 0x03;
1438 s = Utf8State::F4;
1439 } else {
1440 cont = ContentKind::Arbitrary;
1442 } else if (utf8Content != nullptr
1443 && cont != ContentKind::Arbitrary)
1445 utf8Content->push_back(c);
1447 break;
1448 case Utf8State::E0:
1449 if (c >= 0xA0 && c <= 0xBF) {
1450 val = (val << 6) | (c & 0x3F);
1451 s = Utf8State::Trail1;
1452 } else {
1453 cont = ContentKind::Arbitrary;
1454 s = Utf8State::Start;
1456 break;
1457 case Utf8State::EB:
1458 if (c >= 0x80 && c <= 0x9F) {
1459 val = (val << 6) | (c & 0x3F);
1460 s = Utf8State::Trail1;
1461 } else {
1462 cont = ContentKind::Arbitrary;
1463 s = Utf8State::Start;
1465 break;
1466 case Utf8State::F0:
1467 if (c >= 0x90 && c <= 0xBF) {
1468 val = (val << 6) | (c & 0x3F);
1469 s = Utf8State::Trail2;
1470 } else {
1471 cont = ContentKind::Arbitrary;
1472 s = Utf8State::Start;
1474 break;
1475 case Utf8State::F4:
1476 if (c >= 0x80 && c <= 0x8F) {
1477 val = (val << 6) | (c & 0x3F);
1478 s = Utf8State::Trail2;
1479 } else {
1480 cont = ContentKind::Arbitrary;
1481 s = Utf8State::Start;
1483 break;
1484 case Utf8State::Trail1:
1485 if (c >= 0x80 && c <= 0xBF) {
1486 cont = ContentKind::Utf8;
1487 if (utf8Content != nullptr)
1489 utf8Content->push_back((val << 6) | (c & 0x3F));
1490 val = 0;
1492 } else {
1493 cont = ContentKind::Arbitrary;
1495 s = Utf8State::Start;
1496 break;
1497 case Utf8State::Trail2:
1498 if (c >= 0x80 && c <= 0xBF) {
1499 val = (val << 6) | (c & 0x3F);
1500 s = Utf8State::Trail1;
1501 } else {
1502 cont = ContentKind::Arbitrary;
1503 s = Utf8State::Start;
1505 break;
1506 case Utf8State::Trail3:
1507 if (c >= 0x80 && c <= 0xBF) {
1508 val = (val << 6) | (c & 0x3F);
1509 s = Utf8State::Trail2;
1510 } else {
1511 cont = ContentKind::Arbitrary;
1512 s = Utf8State::Start;
1514 break;
1517 *size = n;
1518 *nonArray = isPtr;
1519 *content = cont;
1520 *embeddedNuls = emb;
1521 *terminatingNul = true;
1522 return true;
1524 APValue v;
1525 if (!expr->isCXX11ConstantExpr(compiler.getASTContext(), &v)) {
1526 return false;
1528 switch (v.getKind()) {
1529 case APValue::LValue:
1531 Expr const * e = v.getLValueBase().dyn_cast<Expr const *>();
1532 if (e == nullptr) {
1533 return false;
1535 if (!v.getLValueOffset().isZero()) {
1536 return false; //TODO
1538 Expr const * e2 = e->IgnoreParenImpCasts();
1539 if (e2 != e) {
1540 return isStringConstant(
1541 e2, size, nonArray, content, embeddedNuls, terminatingNul);
1543 //TODO: string literals are represented as recursive LValues???
1544 llvm::APInt n
1545 = compiler.getASTContext().getAsConstantArrayType(t)->getSize();
1546 assert(n != 0);
1547 --n;
1548 assert(n.ule(std::numeric_limits<unsigned>::max()));
1549 *size = static_cast<unsigned>(n.getLimitedValue());
1550 *nonArray = isPtr || *nonArray;
1551 *content = ContentKind::Ascii; //TODO
1552 *embeddedNuls = false; //TODO
1553 *terminatingNul = true;
1554 return true;
1556 case APValue::Array:
1558 if (v.hasArrayFiller()) { //TODO: handle final NULL filler?
1559 return false;
1561 unsigned n = v.getArraySize();
1562 assert(n != 0);
1563 ContentKind cont = ContentKind::Ascii;
1564 bool emb = false;
1565 //TODO: check for ContentType::Utf8
1566 for (unsigned i = 0; i != n - 1; ++i) {
1567 APValue e(v.getArrayInitializedElt(i));
1568 if (!e.isInt()) { //TODO: assert?
1569 return false;
1571 APSInt iv = e.getInt();
1572 if (iv == 0) {
1573 emb = true;
1574 } else if (iv.uge(0x80)) {
1575 cont = ContentKind::Arbitrary;
1578 APValue e(v.getArrayInitializedElt(n - 1));
1579 if (!e.isInt()) { //TODO: assert?
1580 return false;
1582 bool trm = e.getInt() == 0;
1583 *size = trm ? n - 1 : n;
1584 *nonArray = isPtr;
1585 *content = cont;
1586 *embeddedNuls = emb;
1587 *terminatingNul = trm;
1588 return true;
1590 default:
1591 assert(false); //TODO???
1592 return false;
1596 bool StringConstant::isZero(Expr const * expr) {
1597 APSInt res;
1598 return compat::EvaluateAsInt(expr, res, compiler.getASTContext()) && res == 0;
1601 void StringConstant::reportChange(
1602 Expr const * expr, ChangeKind kind, std::string const & original,
1603 std::string const & replacement, PassThrough pass, bool nonArray,
1604 char const * rewriteFrom, char const * rewriteTo)
1606 assert((rewriteFrom == nullptr) == (rewriteTo == nullptr));
1607 if (pass != PassThrough::No && !calls_.empty()) {
1608 Expr const * call = calls_.top();
1609 CallExpr::const_arg_iterator argsBeg;
1610 CallExpr::const_arg_iterator argsEnd;
1611 if (isa<CallExpr>(call)) {
1612 argsBeg = cast<CallExpr>(call)->arg_begin();
1613 argsEnd = cast<CallExpr>(call)->arg_end();
1614 } else if (isa<CXXConstructExpr>(call)) {
1615 argsBeg = cast<CXXConstructExpr>(call)->arg_begin();
1616 argsEnd = cast<CXXConstructExpr>(call)->arg_end();
1617 } else {
1618 assert(false);
1620 for (auto i(argsBeg); i != argsEnd; ++i) {
1621 Expr const * e = (*i)->IgnoreParenImpCasts();
1622 if (isa<CXXBindTemporaryExpr>(e)) {
1623 e = cast<CXXBindTemporaryExpr>(e)->getSubExpr()
1624 ->IgnoreParenImpCasts();
1626 if (e == expr) {
1627 if (isa<CallExpr>(call)) {
1628 FunctionDecl const * fdecl
1629 = cast<CallExpr>(call)->getDirectCallee();
1630 if (fdecl == nullptr) {
1631 break;
1633 loplugin::DeclCheck dc(fdecl);
1634 if (pass == PassThrough::EmptyConstantString) {
1635 if ((dc.Function("equals").Class("OUString")
1636 .Namespace("rtl").GlobalNamespace())
1637 || (dc.Operator(OO_EqualEqual).Namespace("rtl")
1638 .GlobalNamespace()))
1640 report(
1641 DiagnosticsEngine::Warning,
1642 ("rewrite call of '%0' with call of %1 with"
1643 " empty string constant argument as call of"
1644 " 'rtl::OUString::isEmpty'"),
1645 getMemberLocation(call))
1646 << fdecl->getQualifiedNameAsString() << original
1647 << call->getSourceRange();
1648 return;
1650 if (dc.Operator(OO_ExclaimEqual).Namespace("rtl")
1651 .GlobalNamespace())
1653 report(
1654 DiagnosticsEngine::Warning,
1655 ("rewrite call of '%0' with call of %1 with"
1656 " empty string constant argument as call of"
1657 " '!rtl::OUString::isEmpty'"),
1658 getMemberLocation(call))
1659 << fdecl->getQualifiedNameAsString() << original
1660 << call->getSourceRange();
1661 return;
1663 if ((dc.Operator(OO_Plus).Namespace("rtl")
1664 .GlobalNamespace())
1665 || (dc.Operator(OO_Plus).Class("OUString")
1666 .Namespace("rtl").GlobalNamespace()))
1668 report(
1669 DiagnosticsEngine::Warning,
1670 ("call of '%0' with suspicious call of %1 with"
1671 " empty string constant argument"),
1672 getMemberLocation(call))
1673 << fdecl->getQualifiedNameAsString() << original
1674 << call->getSourceRange();
1675 return;
1677 if (dc.Operator(OO_Equal).Class("OUString")
1678 .Namespace("rtl").GlobalNamespace())
1680 report(
1681 DiagnosticsEngine::Warning,
1682 ("rewrite call of '%0' with call of %1 with"
1683 " empty string constant argument as call of"
1684 " rtl::OUString::call"),
1685 getMemberLocation(call))
1686 << fdecl->getQualifiedNameAsString() << original
1687 << call->getSourceRange();
1688 return;
1690 report(
1691 DiagnosticsEngine::Warning,
1692 "TODO call inside %0", getMemberLocation(expr))
1693 << fdecl->getQualifiedNameAsString()
1694 << expr->getSourceRange();
1695 return;
1696 } else {
1697 assert(pass == PassThrough::NonEmptyConstantString);
1698 if ((dc.Function("equals").Class("OUString")
1699 .Namespace("rtl").GlobalNamespace())
1700 || (dc.Operator(OO_Equal).Class("OUString")
1701 .Namespace("rtl").GlobalNamespace())
1702 || (dc.Operator(OO_EqualEqual).Namespace("rtl")
1703 .GlobalNamespace())
1704 || (dc.Operator(OO_ExclaimEqual).Namespace("rtl")
1705 .GlobalNamespace()))
1707 report(
1708 DiagnosticsEngine::Warning,
1709 "elide call of %0 with %1 in call of '%2'",
1710 getMemberLocation(expr))
1711 << original << describeChangeKind(kind)
1712 << fdecl->getQualifiedNameAsString()
1713 << expr->getSourceRange();
1714 return;
1716 report(
1717 DiagnosticsEngine::Warning,
1718 ("rewrite call of %0 with %1 in call of '%2' as"
1719 " (implicit) construction of 'OUString'"),
1720 getMemberLocation(expr))
1721 << original << describeChangeKind(kind)
1722 << fdecl->getQualifiedNameAsString()
1723 << expr->getSourceRange();
1724 return;
1726 } else if (isa<CXXConstructExpr>(call)) {
1727 auto classdecl = cast<CXXConstructExpr>(call)
1728 ->getConstructor()->getParent();
1729 loplugin::DeclCheck dc(classdecl);
1730 if (dc.Class("OUString").Namespace("rtl").GlobalNamespace()
1731 || (dc.Class("OUStringBuffer").Namespace("rtl")
1732 .GlobalNamespace()))
1734 //TODO: propagate further out?
1735 if (pass == PassThrough::EmptyConstantString) {
1736 report(
1737 DiagnosticsEngine::Warning,
1738 ("rewrite construction of %0 with call of %1"
1739 " with empty string constant argument as"
1740 " default construction of %0"),
1741 getMemberLocation(call))
1742 << classdecl << original
1743 << call->getSourceRange();
1744 } else {
1745 assert(pass == PassThrough::NonEmptyConstantString);
1746 report(
1747 DiagnosticsEngine::Warning,
1748 ("elide call of %0 with %1 in construction of"
1749 " %2"),
1750 getMemberLocation(expr))
1751 << original << describeChangeKind(kind)
1752 << classdecl << expr->getSourceRange();
1754 return;
1756 } else {
1757 assert(false);
1762 if (rewriter != nullptr && !nonArray && rewriteFrom != nullptr) {
1763 SourceLocation loc = getMemberLocation(expr);
1764 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
1765 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
1767 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
1768 loc = compiler.getSourceManager().getSpellingLoc(loc);
1770 unsigned n = Lexer::MeasureTokenLength(
1771 loc, compiler.getSourceManager(), compiler.getLangOpts());
1772 if ((std::string(compiler.getSourceManager().getCharacterData(loc), n)
1773 == rewriteFrom)
1774 && replaceText(loc, n, rewriteTo))
1776 return;
1779 report(
1780 DiagnosticsEngine::Warning,
1781 "rewrite call of '%0' with %1 as call of '%2'%3",
1782 getMemberLocation(expr))
1783 << original << describeChangeKind(kind) << replacement
1784 << adviseNonArray(nonArray) << expr->getSourceRange();
1787 void StringConstant::checkEmpty(
1788 CallExpr const * expr, FunctionDecl const * callee, TreatEmpty treatEmpty,
1789 unsigned size, std::string * replacement)
1791 assert(replacement != nullptr);
1792 if (size == 0) {
1793 switch (treatEmpty) {
1794 case TreatEmpty::DefaultCtor:
1795 *replacement = "rtl::OUString default constructor";
1796 break;
1797 case TreatEmpty::CheckEmpty:
1798 *replacement = "rtl::OUString::isEmpty";
1799 break;
1800 case TreatEmpty::Error:
1801 report(
1802 DiagnosticsEngine::Warning,
1803 "call of '%0' with suspicious empty string constant argument",
1804 getMemberLocation(expr))
1805 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1806 break;
1811 void StringConstant::handleChar(
1812 CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
1813 std::string const & replacement, TreatEmpty treatEmpty, bool literal,
1814 char const * rewriteFrom, char const * rewriteTo)
1816 unsigned n;
1817 bool nonArray;
1818 ContentKind cont;
1819 bool emb;
1820 bool trm;
1821 if (!isStringConstant(
1822 expr->getArg(arg)->IgnoreParenImpCasts(), &n, &nonArray, &cont,
1823 &emb, &trm))
1825 return;
1827 if (cont != ContentKind::Ascii) {
1828 report(
1829 DiagnosticsEngine::Warning,
1830 ("call of '%0' with string constant argument containing non-ASCII"
1831 " characters"),
1832 getMemberLocation(expr))
1833 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1834 return;
1836 if (emb) {
1837 report(
1838 DiagnosticsEngine::Warning,
1839 ("call of '%0' with string constant argument containing embedded"
1840 " NULLs"),
1841 getMemberLocation(expr))
1842 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1843 return;
1845 if (!trm) {
1846 report(
1847 DiagnosticsEngine::Warning,
1848 ("call of '%0' with string constant argument lacking a terminating"
1849 " NULL"),
1850 getMemberLocation(expr))
1851 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1852 return;
1854 std::string repl(replacement);
1855 checkEmpty(expr, callee, treatEmpty, n, &repl);
1856 if (!repl.empty()) {
1857 reportChange(
1858 expr, ChangeKind::Char, callee->getQualifiedNameAsString(), repl,
1859 (literal
1860 ? (n == 0
1861 ? PassThrough::EmptyConstantString
1862 : PassThrough::NonEmptyConstantString)
1863 : PassThrough::No),
1864 nonArray, rewriteFrom, rewriteTo);
1868 void StringConstant::handleCharLen(
1869 CallExpr const * expr, unsigned arg1, unsigned arg2,
1870 FunctionDecl const * callee, std::string const & replacement,
1871 TreatEmpty treatEmpty)
1873 // Especially for f(RTL_CONSTASCII_STRINGPARAM("foo")), where
1874 // RTL_CONSTASCII_STRINGPARAM expands to complicated expressions involving
1875 // (&(X)[0] sub-expressions (and it might or might not be better to handle
1876 // that at the level of non-expanded macros instead, but I have not found
1877 // out how to do that yet anyway):
1878 unsigned n;
1879 bool nonArray;
1880 ContentKind cont;
1881 bool emb;
1882 bool trm;
1883 if (!(isStringConstant(
1884 expr->getArg(arg1)->IgnoreParenImpCasts(), &n, &nonArray, &cont,
1885 &emb, &trm)
1886 && trm))
1888 return;
1890 APSInt res;
1891 if (compat::EvaluateAsInt(expr->getArg(arg2), res, compiler.getASTContext())) {
1892 if (res != n) {
1893 return;
1895 } else {
1896 UnaryOperator const * op = dyn_cast<UnaryOperator>(
1897 expr->getArg(arg1)->IgnoreParenImpCasts());
1898 if (op == nullptr || op->getOpcode() != UO_AddrOf) {
1899 return;
1901 ArraySubscriptExpr const * subs = dyn_cast<ArraySubscriptExpr>(
1902 op->getSubExpr()->IgnoreParenImpCasts());
1903 if (subs == nullptr) {
1904 return;
1906 unsigned n2;
1907 bool nonArray2;
1908 ContentKind cont2;
1909 bool emb2;
1910 bool trm2;
1911 if (!(isStringConstant(
1912 subs->getBase()->IgnoreParenImpCasts(), &n2, &nonArray2,
1913 &cont2, &emb2, &trm2)
1914 && n2 == n && cont2 == cont && emb2 == emb && trm2 == trm
1915 //TODO: same strings
1916 && compat::EvaluateAsInt(subs->getIdx(), res, compiler.getASTContext())
1917 && res == 0))
1919 return;
1922 if (cont != ContentKind::Ascii) {
1923 report(
1924 DiagnosticsEngine::Warning,
1925 ("call of '%0' with string constant argument containing non-ASCII"
1926 " characters"),
1927 getMemberLocation(expr))
1928 << callee->getQualifiedNameAsString() << expr->getSourceRange();
1930 if (emb) {
1931 return;
1933 std::string repl(replacement);
1934 checkEmpty(expr, callee, treatEmpty, n, &repl);
1935 reportChange(
1936 expr, ChangeKind::CharLen, callee->getQualifiedNameAsString(), repl,
1937 PassThrough::No, nonArray, nullptr, nullptr);
1940 void StringConstant::handleOUStringCtor(
1941 CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
1942 bool explicitFunctionalCastNotation)
1944 handleOUStringCtor(expr, expr->getArg(arg), callee, explicitFunctionalCastNotation);
1947 void StringConstant::handleOStringCtor(
1948 CallExpr const * expr, unsigned arg, FunctionDecl const * callee,
1949 bool explicitFunctionalCastNotation)
1951 handleOStringCtor(expr, expr->getArg(arg), callee, explicitFunctionalCastNotation);
1954 void StringConstant::handleOUStringCtor(
1955 Expr const * expr, Expr const * argExpr, FunctionDecl const * callee,
1956 bool explicitFunctionalCastNotation)
1958 handleStringCtor(expr, argExpr, callee, explicitFunctionalCastNotation, StringKind::Unicode);
1961 void StringConstant::handleOStringCtor(
1962 Expr const * expr, Expr const * argExpr, FunctionDecl const * callee,
1963 bool explicitFunctionalCastNotation)
1965 handleStringCtor(expr, argExpr, callee, explicitFunctionalCastNotation, StringKind::Char);
1968 void StringConstant::handleStringCtor(
1969 Expr const * expr, Expr const * argExpr, FunctionDecl const * callee,
1970 bool explicitFunctionalCastNotation, StringKind stringKind)
1972 auto e0 = argExpr->IgnoreParenImpCasts();
1973 auto e1 = dyn_cast<CXXFunctionalCastExpr>(e0);
1974 if (e1 == nullptr) {
1975 if (explicitFunctionalCastNotation) {
1976 return;
1978 } else {
1979 e0 = e1->getSubExpr()->IgnoreParenImpCasts();
1981 auto e2 = dyn_cast<CXXBindTemporaryExpr>(e0);
1982 if (e2 == nullptr) {
1983 return;
1985 auto e3 = dyn_cast<CXXConstructExpr>(
1986 e2->getSubExpr()->IgnoreParenImpCasts());
1987 if (e3 == nullptr) {
1988 return;
1990 if (!loplugin::DeclCheck(e3->getConstructor()).MemberFunction()
1991 .Class(stringKind == StringKind::Unicode ? "OUString" : "OString").Namespace("rtl").GlobalNamespace())
1993 return;
1995 if (e3->getNumArgs() == 0) {
1996 report(
1997 DiagnosticsEngine::Warning,
1998 ("in call of '%0', replace default-constructed 'OUString' with an"
1999 " empty string literal"),
2000 e3->getExprLoc())
2001 << callee->getQualifiedNameAsString() << expr->getSourceRange();
2002 return;
2004 if (e3->getNumArgs() == 1
2005 && e3->getConstructor()->getNumParams() == 1
2006 && (loplugin::TypeCheck(
2007 e3->getConstructor()->getParamDecl(0)->getType())
2008 .Typedef(stringKind == StringKind::Unicode ? "sal_Unicode" : "char").GlobalNamespace()))
2010 // It may not be easy to rewrite OUString(c), esp. given there is no
2011 // OUString ctor taking an OUStringChar arg, so don't warn there:
2012 if (!explicitFunctionalCastNotation) {
2013 report(
2014 DiagnosticsEngine::Warning,
2015 ("in call of '%0', replace 'OUString' constructed from a"
2016 " 'sal_Unicode' with an 'OUStringChar'"),
2017 e3->getExprLoc())
2018 << callee->getQualifiedNameAsString() << expr->getSourceRange();
2020 return;
2022 if (e3->getNumArgs() != 2) {
2023 return;
2025 unsigned n;
2026 bool nonArray;
2027 ContentKind cont;
2028 bool emb;
2029 bool trm;
2030 if (!isStringConstant(
2031 e3->getArg(0)->IgnoreParenImpCasts(), &n, &nonArray, &cont, &emb,
2032 &trm))
2034 return;
2036 //TODO: cont, emb, trm
2037 if (rewriter != nullptr) {
2038 auto loc1 = compat::getBeginLoc(e3);
2039 auto range = e3->getParenOrBraceRange();
2040 if (loc1.isFileID() && range.getBegin().isFileID()
2041 && range.getEnd().isFileID())
2043 auto loc2 = range.getBegin();
2044 for (bool first = true;; first = false) {
2045 unsigned n = Lexer::MeasureTokenLength(
2046 loc2, compiler.getSourceManager(), compiler.getLangOpts());
2047 if (!first) {
2048 StringRef s(
2049 compiler.getSourceManager().getCharacterData(loc2), n);
2050 while (s.startswith("\\\n")) {
2051 s = s.drop_front(2);
2052 while (!s.empty()
2053 && (s.front() == ' ' || s.front() == '\t'
2054 || s.front() == '\n' || s.front() == '\v'
2055 || s.front() == '\f'))
2057 s = s.drop_front(1);
2060 if (!(s.empty() || s.startswith("/*") || s.startswith("//")
2061 || s == "\\"))
2063 break;
2066 loc2 = loc2.getLocWithOffset(std::max<unsigned>(n, 1));
2068 auto loc3 = range.getEnd();
2069 for (;;) {
2070 auto l = Lexer::GetBeginningOfToken(
2071 loc3.getLocWithOffset(-1), compiler.getSourceManager(),
2072 compiler.getLangOpts());
2073 unsigned n = Lexer::MeasureTokenLength(
2074 l, compiler.getSourceManager(), compiler.getLangOpts());
2075 StringRef s(compiler.getSourceManager().getCharacterData(l), n);
2076 while (s.startswith("\\\n")) {
2077 s = s.drop_front(2);
2078 while (!s.empty()
2079 && (s.front() == ' ' || s.front() == '\t'
2080 || s.front() == '\n' || s.front() == '\v'
2081 || s.front() == '\f'))
2083 s = s.drop_front(1);
2086 if (!(s.empty() || s.startswith("/*") || s.startswith("//")
2087 || s == "\\"))
2089 break;
2091 loc3 = l;
2093 if (removeText(CharSourceRange(SourceRange(loc1, loc2), false))) {
2094 if (removeText(SourceRange(loc3, range.getEnd()))) {
2095 return;
2097 report(DiagnosticsEngine::Fatal, "Corrupt rewrite", loc3)
2098 << expr->getSourceRange();
2099 return;
2103 report(
2104 DiagnosticsEngine::Warning,
2105 ("in call of '%0', replace 'OUString' constructed from a string literal"
2106 " directly with the string literal"),
2107 e3->getExprLoc())
2108 << callee->getQualifiedNameAsString() << expr->getSourceRange();
2111 void StringConstant::handleFunArgOstring(
2112 CallExpr const * expr, unsigned arg, FunctionDecl const * callee)
2114 auto argExpr = expr->getArg(arg)->IgnoreParenImpCasts();
2115 unsigned n;
2116 bool nonArray;
2117 ContentKind cont;
2118 bool emb;
2119 bool trm;
2120 if (isStringConstant(argExpr, &n, &nonArray, &cont, &emb, &trm)) {
2121 if (cont != ContentKind::Ascii || emb) {
2122 return;
2124 if (!trm) {
2125 report(
2126 DiagnosticsEngine::Warning,
2127 ("call of '%0' with string constant argument lacking a"
2128 " terminating NULL"),
2129 getMemberLocation(expr))
2130 << callee->getQualifiedNameAsString() << expr->getSourceRange();
2131 return;
2133 std::string repl;
2134 checkEmpty(expr, callee, TreatEmpty::Error, n, &repl);
2135 if (nonArray) {
2136 report(
2137 DiagnosticsEngine::Warning,
2138 ("in call of '%0' with non-array string constant argument,"
2139 " turn the non-array string constant into an array"),
2140 getMemberLocation(expr))
2141 << callee->getQualifiedNameAsString() << expr->getSourceRange();
2143 } else if (auto cexpr = lookForCXXConstructExpr(argExpr)) {
2144 auto classdecl = cexpr->getConstructor()->getParent();
2145 if (loplugin::DeclCheck(classdecl).Class("OString").Namespace("rtl")
2146 .GlobalNamespace())
2148 switch (cexpr->getConstructor()->getNumParams()) {
2149 case 0:
2150 report(
2151 DiagnosticsEngine::Warning,
2152 ("in call of '%0', replace empty %1 constructor with empty"
2153 " string literal"),
2154 cexpr->getLocation())
2155 << callee->getQualifiedNameAsString() << classdecl
2156 << expr->getSourceRange();
2157 break;
2158 case 2:
2159 if (isStringConstant(
2160 cexpr->getArg(0)->IgnoreParenImpCasts(), &n, &nonArray,
2161 &cont, &emb, &trm))
2163 APSInt res;
2164 if (compat::EvaluateAsInt(cexpr->getArg(1),
2165 res, compiler.getASTContext()))
2167 if (res == n && !emb && trm) {
2168 report(
2169 DiagnosticsEngine::Warning,
2170 ("in call of '%0', elide explicit %1"
2171 " constructor%2"),
2172 cexpr->getLocation())
2173 << callee->getQualifiedNameAsString()
2174 << classdecl << adviseNonArray(nonArray)
2175 << expr->getSourceRange();
2177 } else {
2178 if (emb) {
2179 report(
2180 DiagnosticsEngine::Warning,
2181 ("call of %0 constructor with string constant"
2182 " argument containing embedded NULLs"),
2183 cexpr->getLocation())
2184 << classdecl << cexpr->getSourceRange();
2185 return;
2187 if (!trm) {
2188 report(
2189 DiagnosticsEngine::Warning,
2190 ("call of %0 constructor with string constant"
2191 " argument lacking a terminating NULL"),
2192 cexpr->getLocation())
2193 << classdecl << cexpr->getSourceRange();
2194 return;
2196 report(
2197 DiagnosticsEngine::Warning,
2198 "in call of '%0', elide explicit %1 constructor%2",
2199 cexpr->getLocation())
2200 << callee->getQualifiedNameAsString() << classdecl
2201 << adviseNonArray(nonArray)
2202 << expr->getSourceRange();
2205 break;
2206 default:
2207 break;
2213 loplugin::Plugin::Registration< StringConstant > X("stringconstant", true);
2217 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */