1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
25 // Define a "string constant" to be a constant expression either of type "array
26 // of N char" where each array element is a non-NUL ASCII character---except
27 // that the last array element may be NUL, or, in some situations, of type char
28 // with a ASCII value (including NUL). Note that the former includes
29 // expressions denoting narrow string literals like "foo", and, with toolchains
30 // that support constexpr, constexpr variables declared like
32 // constexpr char str[] = "bar";
34 // This plugin flags uses of OUString functions with string constant arguments
35 // that can be rewritten more directly, like
37 // OUString::createFromAscii("foo") -> "foo"
41 // s.equals(OUString("bar")) -> s == "bar"
45 SourceLocation
getMemberLocation(Expr
const * expr
) {
46 CallExpr
const * e1
= dyn_cast
<CallExpr
>(expr
);
47 MemberExpr
const * e2
= e1
== nullptr
48 ? nullptr : dyn_cast
<MemberExpr
>(e1
->getCallee());
49 return e2
== nullptr ? expr
->getExprLoc()/*TODO*/ : e2
->getMemberLoc();
52 bool isLhsOfAssignment(FunctionDecl
const * decl
, unsigned parameter
) {
56 auto oo
= decl
->getOverloadedOperator();
58 || (oo
>= OO_PlusEqual
&& oo
<= OO_GreaterGreaterEqual
);
61 bool hasOverloads(FunctionDecl
const * decl
, unsigned arguments
) {
63 auto ctx
= decl
->getDeclContext();
64 if (ctx
->getDeclKind() == Decl::LinkageSpec
) {
65 ctx
= ctx
->getParent();
67 auto res
= ctx
->lookup(decl
->getDeclName());
68 for (auto d
= res
.begin(); d
!= res
.end(); ++d
) {
69 FunctionDecl
const * f
= dyn_cast
<FunctionDecl
>(*d
);
70 if (f
!= nullptr && f
->getMinRequiredArguments() <= arguments
71 && f
->getNumParams() >= arguments
)
73 auto consDecl
= dyn_cast
<CXXConstructorDecl
>(f
);
74 if (consDecl
&& consDecl
->isCopyOrMoveConstructor()) {
86 CXXConstructExpr
const * lookForCXXConstructExpr(Expr
const * expr
) {
87 if (auto e
= dyn_cast
<MaterializeTemporaryExpr
>(expr
)) {
88 expr
= e
->GetTemporaryExpr();
90 if (auto e
= dyn_cast
<CXXFunctionalCastExpr
>(expr
)) {
91 expr
= e
->getSubExpr();
93 if (auto e
= dyn_cast
<CXXBindTemporaryExpr
>(expr
)) {
94 expr
= e
->getSubExpr();
96 return dyn_cast
<CXXConstructExpr
>(expr
);
99 char const * adviseNonArray(bool nonArray
) {
101 ? ", and turn the non-array string constant into an array" : "";
104 class StringConstant
:
105 public RecursiveASTVisitor
<StringConstant
>, public loplugin::RewritePlugin
108 explicit StringConstant(loplugin::InstantiationData
const & data
):
109 RewritePlugin(data
) {}
113 bool TraverseCallExpr(CallExpr
* expr
);
115 bool TraverseCXXMemberCallExpr(CXXMemberCallExpr
* expr
);
117 bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr
* expr
);
119 bool TraverseCXXConstructExpr(CXXConstructExpr
* expr
);
121 bool VisitCallExpr(CallExpr
const * expr
);
123 bool VisitCXXConstructExpr(CXXConstructExpr
const * expr
);
126 enum class ContentKind
{ Ascii
, Utf8
, Arbitrary
};
128 enum class TreatEmpty
{ DefaultCtor
, CheckEmpty
, Error
};
130 enum class ChangeKind
{ Char
, CharLen
, SingleChar
, OUStringLiteral1
};
132 enum class PassThrough
{ No
, EmptyConstantString
, NonEmptyConstantString
};
134 std::string
describeChangeKind(ChangeKind kind
);
136 bool isStringConstant(
137 Expr
const * expr
, unsigned * size
, bool * nonArray
,
138 ContentKind
* content
, bool * embeddedNuls
, bool * terminatingNul
,
139 std::vector
<char32_t
> * utf8Content
= nullptr);
141 bool isZero(Expr
const * expr
);
144 Expr
const * expr
, ChangeKind kind
, std::string
const & original
,
145 std::string
const & replacement
, PassThrough pass
, bool nonArray
,
146 char const * rewriteFrom
, char const * rewriteTo
);
149 CallExpr
const * expr
, FunctionDecl
const * callee
,
150 TreatEmpty treatEmpty
, unsigned size
, std::string
* replacement
);
153 CallExpr
const * expr
, unsigned arg
, FunctionDecl
const * callee
,
154 std::string
const & replacement
, TreatEmpty treatEmpty
, bool literal
,
155 char const * rewriteFrom
= nullptr, char const * rewriteTo
= nullptr);
158 CallExpr
const * expr
, unsigned arg1
, unsigned arg2
,
159 FunctionDecl
const * callee
, std::string
const & replacement
,
160 TreatEmpty treatEmpty
);
162 void handleOUStringCtor(
163 CallExpr
const * expr
, unsigned arg
, FunctionDecl
const * callee
,
164 bool explicitFunctionalCastNotation
);
166 void handleOUStringCtor(
167 Expr
const * expr
, Expr
const * argExpr
, FunctionDecl
const * callee
,
168 bool explicitFunctionalCastNotation
);
170 void handleFunArgOstring(
171 CallExpr
const * expr
, unsigned arg
, FunctionDecl
const * callee
);
173 std::stack
<Expr
const *> calls_
;
176 void StringConstant::run() {
177 if (compiler
.getLangOpts().CPlusPlus
178 && compiler
.getPreprocessor().getIdentifierInfo(
179 "LIBO_INTERNAL_ONLY")->hasMacroDefinition())
180 //TODO: some parts of it are useful for external code, too
182 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
186 bool StringConstant::TraverseCallExpr(CallExpr
* expr
) {
187 if (!WalkUpFromCallExpr(expr
)) {
192 for (auto * e
: expr
->children()) {
193 if (!TraverseStmt(e
)) {
202 bool StringConstant::TraverseCXXMemberCallExpr(CXXMemberCallExpr
* expr
) {
203 if (!WalkUpFromCXXMemberCallExpr(expr
)) {
208 for (auto * e
: expr
->children()) {
209 if (!TraverseStmt(e
)) {
218 bool StringConstant::TraverseCXXOperatorCallExpr(CXXOperatorCallExpr
* expr
)
220 if (!WalkUpFromCXXOperatorCallExpr(expr
)) {
225 for (auto * e
: expr
->children()) {
226 if (!TraverseStmt(e
)) {
235 bool StringConstant::TraverseCXXConstructExpr(CXXConstructExpr
* expr
) {
236 if (!WalkUpFromCXXConstructExpr(expr
)) {
241 for (auto * e
: expr
->children()) {
242 if (!TraverseStmt(e
)) {
251 bool StringConstant::VisitCallExpr(CallExpr
const * expr
) {
252 if (ignoreLocation(expr
)) {
255 FunctionDecl
const * fdecl
= expr
->getDirectCallee();
256 if (fdecl
== nullptr) {
259 for (unsigned i
= 0; i
!= fdecl
->getNumParams(); ++i
) {
260 auto t
= fdecl
->getParamDecl(i
)->getType();
261 if (loplugin::TypeCheck(t
).NotSubstTemplateTypeParmType()
262 .LvalueReference().Const().NotSubstTemplateTypeParmType()
263 .Class("OUString").Namespace("rtl").GlobalNamespace())
265 if (!(isLhsOfAssignment(fdecl
, i
)
266 || hasOverloads(fdecl
, expr
->getNumArgs())))
268 handleOUStringCtor(expr
, i
, fdecl
, true);
272 loplugin::DeclCheck
dc(fdecl
);
273 //TODO: u.compareToAscii("foo") -> u.???("foo")
274 //TODO: u.compareToIgnoreAsciiCaseAscii("foo") -> u.???("foo")
275 if ((dc
.Function("createFromAscii").Class("OUString").Namespace("rtl")
277 && fdecl
->getNumParams() == 1)
279 // OUString::createFromAscii("foo") -> OUString("foo")
281 expr
, 0, fdecl
, "rtl::OUString constructor",
282 TreatEmpty::DefaultCtor
, true);
285 if ((dc
.Function("endsWithAsciiL").Class("OUString").Namespace("rtl")
287 && fdecl
->getNumParams() == 2)
289 // u.endsWithAsciiL("foo", 3) -> u.endsWith("foo"):
291 expr
, 0, 1, fdecl
, "rtl::OUString::endsWith", TreatEmpty::Error
);
294 if ((dc
.Function("endsWithIgnoreAsciiCaseAsciiL").Class("OUString")
295 .Namespace("rtl").GlobalNamespace())
296 && fdecl
->getNumParams() == 2)
298 // u.endsWithIgnoreAsciiCaseAsciiL("foo", 3) ->
299 // u.endsWithIgnoreAsciiCase("foo"):
301 expr
, 0, 1, fdecl
, "rtl::OUString::endsWithIgnoreAsciiCase",
305 if ((dc
.Function("equalsAscii").Class("OUString").Namespace("rtl")
307 && fdecl
->getNumParams() == 1)
309 // u.equalsAscii("foo") -> u == "foo":
311 expr
, 0, fdecl
, "operator ==", TreatEmpty::CheckEmpty
, false);
314 if ((dc
.Function("equalsAsciiL").Class("OUString").Namespace("rtl")
316 && fdecl
->getNumParams() == 2)
318 // u.equalsAsciiL("foo", 3) -> u == "foo":
319 handleCharLen(expr
, 0, 1, fdecl
, "operator ==", TreatEmpty::CheckEmpty
);
322 if ((dc
.Function("equalsIgnoreAsciiCaseAscii").Class("OUString")
323 .Namespace("rtl").GlobalNamespace())
324 && fdecl
->getNumParams() == 1)
326 // u.equalsIgnoreAsciiCaseAscii("foo") ->
327 // u.equalsIngoreAsciiCase("foo"):
328 auto file
= compiler
.getSourceManager().getFilename(
329 compiler
.getSourceManager().getSpellingLoc(expr
->getLocStart()));
330 if (loplugin::isSamePathname(
331 file
, SRCDIR
"/sal/qa/rtl/strings/test_oustring_compare.cxx"))
336 expr
, 0, fdecl
, "rtl::OUString::equalsIgnoreAsciiCase",
337 TreatEmpty::CheckEmpty
, false);
340 if ((dc
.Function("equalsIgnoreAsciiCaseAsciiL").Class("OUString")
341 .Namespace("rtl").GlobalNamespace())
342 && fdecl
->getNumParams() == 2)
344 // u.equalsIgnoreAsciiCaseAsciiL("foo", 3) ->
345 // u.equalsIngoreAsciiCase("foo"):
346 auto file
= compiler
.getSourceManager().getFilename(
347 compiler
.getSourceManager().getSpellingLoc(expr
->getLocStart()));
348 if (loplugin::isSamePathname(
349 file
, SRCDIR
"/sal/qa/rtl/strings/test_oustring_compare.cxx"))
354 expr
, 0, 1, fdecl
, "rtl::OUString::equalsIgnoreAsciiCase",
355 TreatEmpty::CheckEmpty
);
358 if ((dc
.Function("indexOfAsciiL").Class("OUString").Namespace("rtl")
360 && fdecl
->getNumParams() == 3)
362 assert(expr
->getNumArgs() == 3);
363 // u.indexOfAsciiL("foo", 3, i) -> u.indexOf("foo", i):
365 expr
, 0, 1, fdecl
, "rtl::OUString::indexOf", TreatEmpty::Error
);
368 if ((dc
.Function("lastIndexOfAsciiL").Class("OUString").Namespace("rtl")
370 && fdecl
->getNumParams() == 2)
372 // u.lastIndexOfAsciiL("foo", 3) -> u.lastIndexOf("foo"):
374 expr
, 0, 1, fdecl
, "rtl::OUString::lastIndexOf", TreatEmpty::Error
);
377 if ((dc
.Function("matchAsciiL").Class("OUString").Namespace("rtl")
379 && fdecl
->getNumParams() == 3)
381 assert(expr
->getNumArgs() == 3);
382 // u.matchAsciiL("foo", 3, i) -> u.match("foo", i):
385 (isZero(expr
->getArg(2))
386 ? std::string("rtl::OUString::startsWith")
387 : std::string("rtl::OUString::match")),
391 if ((dc
.Function("matchIgnoreAsciiCaseAsciiL").Class("OUString")
392 .Namespace("rtl").GlobalNamespace())
393 && fdecl
->getNumParams() == 3)
395 assert(expr
->getNumArgs() == 3);
396 // u.matchIgnoreAsciiCaseAsciiL("foo", 3, i) ->
397 // u.matchIgnoreAsciiCase("foo", i):
400 (isZero(expr
->getArg(2))
401 ? std::string("rtl::OUString::startsWithIgnoreAsciiCase")
402 : std::string("rtl::OUString::matchIgnoreAsciiCase")),
406 if ((dc
.Function("reverseCompareToAsciiL").Class("OUString")
407 .Namespace("rtl").GlobalNamespace())
408 && fdecl
->getNumParams() == 2)
410 // u.reverseCompareToAsciiL("foo", 3) -> u.reverseCompareTo("foo"):
412 expr
, 0, 1, fdecl
, "rtl::OUString::reverseCompareTo",
416 if ((dc
.Function("reverseCompareTo").Class("OUString").Namespace("rtl")
418 && fdecl
->getNumParams() == 1)
420 handleOUStringCtor(expr
, 0, fdecl
, false);
423 if ((dc
.Function("equalsIgnoreAsciiCase").Class("OUString").Namespace("rtl")
425 && fdecl
->getNumParams() == 1)
427 handleOUStringCtor(expr
, 0, fdecl
, false);
430 if ((dc
.Function("match").Class("OUString").Namespace("rtl")
432 && fdecl
->getNumParams() == 2)
434 handleOUStringCtor(expr
, 0, fdecl
, false);
437 if ((dc
.Function("matchIgnoreAsciiCase").Class("OUString").Namespace("rtl")
439 && fdecl
->getNumParams() == 2)
441 handleOUStringCtor(expr
, 0, fdecl
, false);
444 if ((dc
.Function("startsWith").Class("OUString").Namespace("rtl")
446 && fdecl
->getNumParams() == 2)
448 handleOUStringCtor(expr
, 0, fdecl
, false);
451 if ((dc
.Function("startsWithIgnoreAsciiCase").Class("OUString")
452 .Namespace("rtl").GlobalNamespace())
453 && fdecl
->getNumParams() == 2)
455 handleOUStringCtor(expr
, 0, fdecl
, false);
458 if ((dc
.Function("endsWith").Class("OUString").Namespace("rtl")
460 && fdecl
->getNumParams() == 2)
462 handleOUStringCtor(expr
, 0, fdecl
, false);
465 if ((dc
.Function("endsWithIgnoreAsciiCase").Class("OUString")
466 .Namespace("rtl").GlobalNamespace())
467 && fdecl
->getNumParams() == 2)
469 handleOUStringCtor(expr
, 0, fdecl
, false);
472 if ((dc
.Function("indexOf").Class("OUString").Namespace("rtl")
474 && fdecl
->getNumParams() == 2)
476 handleOUStringCtor(expr
, 0, fdecl
, false);
479 if ((dc
.Function("lastIndexOf").Class("OUString").Namespace("rtl")
481 && fdecl
->getNumParams() == 1)
483 handleOUStringCtor(expr
, 0, fdecl
, false);
486 if ((dc
.Function("replaceFirst").Class("OUString").Namespace("rtl")
488 && fdecl
->getNumParams() == 3)
490 handleOUStringCtor(expr
, 0, fdecl
, false);
491 handleOUStringCtor(expr
, 1, fdecl
, false);
494 if ((dc
.Function("replaceAll").Class("OUString").Namespace("rtl")
496 && (fdecl
->getNumParams() == 2 || fdecl
->getNumParams() == 3))
498 handleOUStringCtor(expr
, 0, fdecl
, false);
499 handleOUStringCtor(expr
, 1, fdecl
, false);
502 if ((dc
.Operator(OO_PlusEqual
).Class("OUString").Namespace("rtl")
504 && fdecl
->getNumParams() == 1)
507 expr
, dyn_cast
<CXXOperatorCallExpr
>(expr
) == nullptr ? 0 : 1,
511 if ((dc
.Function("equals").Class("OUString").Namespace("rtl")
513 && fdecl
->getNumParams() == 1)
520 if (!isStringConstant(
521 expr
->getArg(0)->IgnoreParenImpCasts(), &n
, &nonArray
, &cont
,
526 if (cont
!= ContentKind::Ascii
) {
528 DiagnosticsEngine::Warning
,
529 ("call of '%0' with string constant argument containing"
530 " non-ASCII characters"),
532 << fdecl
->getQualifiedNameAsString() << expr
->getSourceRange();
536 DiagnosticsEngine::Warning
,
537 ("call of '%0' with string constant argument containing"
540 << fdecl
->getQualifiedNameAsString() << expr
->getSourceRange();
544 DiagnosticsEngine::Warning
,
545 ("rewrite call of '%0' with empty string constant argument as"
546 " call of 'rtl::OUString::isEmpty'"),
548 << fdecl
->getQualifiedNameAsString() << expr
->getSourceRange();
552 if (dc
.Operator(OO_EqualEqual
).Namespace("rtl").GlobalNamespace()
553 && fdecl
->getNumParams() == 2)
555 for (unsigned i
= 0; i
!= 2; ++i
) {
561 if (!isStringConstant(
562 expr
->getArg(i
)->IgnoreParenImpCasts(), &n
, &nonArray
,
567 if (cont
!= ContentKind::Ascii
) {
569 DiagnosticsEngine::Warning
,
570 ("call of '%0' with string constant argument containing"
571 " non-ASCII characters"),
573 << fdecl
->getQualifiedNameAsString()
574 << expr
->getSourceRange();
578 DiagnosticsEngine::Warning
,
579 ("call of '%0' with string constant argument containing"
582 << fdecl
->getQualifiedNameAsString()
583 << expr
->getSourceRange();
587 DiagnosticsEngine::Warning
,
588 ("rewrite call of '%0' with empty string constant argument"
589 " as call of 'rtl::OUString::isEmpty'"),
591 << fdecl
->getQualifiedNameAsString()
592 << expr
->getSourceRange();
597 if (dc
.Operator(OO_ExclaimEqual
).Namespace("rtl").GlobalNamespace()
598 && fdecl
->getNumParams() == 2)
600 for (unsigned i
= 0; i
!= 2; ++i
) {
606 if (!isStringConstant(
607 expr
->getArg(i
)->IgnoreParenImpCasts(), &n
, &nonArray
,
612 if (cont
!= ContentKind::Ascii
) {
614 DiagnosticsEngine::Warning
,
615 ("call of '%0' with string constant argument containing"
616 " non-ASCII characters"),
618 << fdecl
->getQualifiedNameAsString()
619 << expr
->getSourceRange();
623 DiagnosticsEngine::Warning
,
624 ("call of '%0' with string constant argument containing"
627 << fdecl
->getQualifiedNameAsString()
628 << expr
->getSourceRange();
632 DiagnosticsEngine::Warning
,
633 ("rewrite call of '%0' with empty string constant argument"
634 " as call of '!rtl::OUString::isEmpty'"),
636 << fdecl
->getQualifiedNameAsString()
637 << expr
->getSourceRange();
642 if (dc
.Operator(OO_Equal
).Namespace("rtl").GlobalNamespace()
643 && fdecl
->getNumParams() == 1)
650 if (!isStringConstant(
651 expr
->getArg(1)->IgnoreParenImpCasts(), &n
, &nonArray
, &cont
,
656 if (cont
!= ContentKind::Ascii
) {
658 DiagnosticsEngine::Warning
,
659 ("call of '%0' with string constant argument containing"
660 " non-ASCII characters"),
662 << fdecl
->getQualifiedNameAsString() << expr
->getSourceRange();
666 DiagnosticsEngine::Warning
,
667 ("call of '%0' with string constant argument containing"
670 << fdecl
->getQualifiedNameAsString() << expr
->getSourceRange();
674 DiagnosticsEngine::Warning
,
675 ("rewrite call of '%0' with empty string constant argument as"
676 " call of 'rtl::OUString::clear'"),
678 << fdecl
->getQualifiedNameAsString() << expr
->getSourceRange();
683 if ((dc
.Function("appendAscii").Class("OUStringBuffer").Namespace("rtl")
685 && fdecl
->getNumParams() == 1)
687 // u.appendAscii("foo") -> u.append("foo")
689 expr
, 0, fdecl
, "rtl::OUStringBuffer::append", TreatEmpty::Error
,
690 true, "appendAscii", "append");
693 if ((dc
.Function("appendAscii").Class("OUStringBuffer").Namespace("rtl")
695 && fdecl
->getNumParams() == 2)
697 // u.appendAscii("foo", 3) -> u.append("foo"):
699 expr
, 0, 1, fdecl
, "rtl::OUStringBuffer::append",
703 if (dc
.Function("append").Class("OStringBuffer").Namespace("rtl")
706 switch (fdecl
->getNumParams()) {
708 handleFunArgOstring(expr
, 0, fdecl
);
712 // b.append("foo", 3) -> b.append("foo"):
713 auto file
= compiler
.getSourceManager().getFilename(
714 compiler
.getSourceManager().getSpellingLoc(
715 expr
->getLocStart()));
716 if (loplugin::isSamePathname(
718 SRCDIR
"/sal/qa/OStringBuffer/rtl_OStringBuffer.cxx"))
723 expr
, 0, 1, fdecl
, "rtl::OStringBuffer::append",
732 if (dc
.Function("insert").Class("OStringBuffer").Namespace("rtl")
735 switch (fdecl
->getNumParams()) {
737 handleFunArgOstring(expr
, 1, fdecl
);
741 // b.insert(i, "foo", 3) -> b.insert(i, "foo"):
743 expr
, 1, 2, fdecl
, "rtl::OStringBuffer::insert",
755 bool StringConstant::VisitCXXConstructExpr(CXXConstructExpr
const * expr
) {
756 if (ignoreLocation(expr
)) {
759 auto classdecl
= expr
->getConstructor()->getParent();
760 if (loplugin::DeclCheck(classdecl
)
761 .Class("OUString").Namespace("rtl").GlobalNamespace())
766 switch (expr
->getConstructor()->getNumParams()) {
768 if (!loplugin::TypeCheck(
769 expr
->getConstructor()->getParamDecl(0)->getType())
770 .Typedef("sal_Unicode").GlobalNamespace())
774 kind
= ChangeKind::SingleChar
;
775 pass
= PassThrough::NonEmptyConstantString
;
780 auto arg
= expr
->getArg(0);
781 if (loplugin::TypeCheck(arg
->getType())
782 .Class("OUStringLiteral1_").Namespace("rtl")
785 kind
= ChangeKind::OUStringLiteral1
;
786 pass
= PassThrough::NonEmptyConstantString
;
794 if (!isStringConstant(
795 arg
->IgnoreParenImpCasts(), &n
, &nonArray
, &cont
,
800 // OSL_THIS_FUNC may be defined as "" or as something other
801 // than a string literal in include/osl/diagnose.h:
802 auto loc
= arg
->getLocStart();
803 if (compiler
.getSourceManager().isMacroBodyExpansion(loc
)
804 && (Lexer::getImmediateMacroName(
805 loc
, compiler
.getSourceManager(),
806 compiler
.getLangOpts())
811 if (cont
!= ContentKind::Ascii
) {
813 DiagnosticsEngine::Warning
,
814 ("construction of %0 with string constant argument"
815 " containing non-ASCII characters"),
817 << classdecl
<< expr
->getSourceRange();
821 DiagnosticsEngine::Warning
,
822 ("construction of %0 with string constant argument"
823 " containing embedded NULs"),
825 << classdecl
<< expr
->getSourceRange();
827 kind
= ChangeKind::Char
;
829 ? PassThrough::EmptyConstantString
830 : PassThrough::NonEmptyConstantString
;
842 std::vector
<char32_t
> utf8Cont
;
843 if (!isStringConstant(
844 expr
->getArg(0)->IgnoreParenImpCasts(), &n
, &nonArray
,
845 &cont
, &emb
, &trm
, &utf8Cont
))
850 if (!expr
->getArg(1)->EvaluateAsInt(
851 res
, compiler
.getASTContext()))
857 DiagnosticsEngine::Warning
,
858 ("suspicious 'rtl::OUString' constructor with literal"
859 " of length %0 and non-matching length argument %1"),
861 << n
<< res
.toString(10) << expr
->getSourceRange();
865 if (!expr
->getArg(2)->EvaluateAsInt(
866 enc
, compiler
.getASTContext()))
870 auto const encIsAscii
= enc
== 11; // RTL_TEXTENCODING_ASCII_US
871 auto const encIsUtf8
= enc
== 76; // RTL_TEXTENCODING_UTF8
872 if (!expr
->getArg(3)->EvaluateAsInt(
873 res
, compiler
.getASTContext())
874 || res
!= 0x333) // OSTRING_TO_OUSTRING_CVTFLAGS
878 if (!encIsAscii
&& cont
== ContentKind::Ascii
) {
880 DiagnosticsEngine::Warning
,
881 ("suspicious 'rtl::OUString' constructor with text"
882 " encoding %0 but plain ASCII content; use"
883 " 'RTL_TEXTENCODING_ASCII_US' instead"),
884 expr
->getArg(2)->getExprLoc())
885 << enc
.toString(10) << expr
->getSourceRange();
889 if (cont
== ContentKind::Arbitrary
) {
891 DiagnosticsEngine::Warning
,
892 ("suspicious 'rtl::OUString' constructor with text"
893 " encoding 'RTL_TEXTENCODING_UTF8' but non-UTF-8"
895 expr
->getArg(0)->getExprLoc())
896 << expr
->getSourceRange();
898 assert(cont
== ContentKind::Utf8
);
899 //TODO: keep original content as much as possible
900 std::ostringstream s
;
901 for (auto const c
: utf8Cont
) {
904 } else if (c
== '"') {
906 } else if (c
== '\a') {
908 } else if (c
== '\b') {
910 } else if (c
== '\f') {
912 } else if (c
== '\n') {
914 } else if (c
== '\r') {
916 } else if (c
== '\t') {
918 } else if (c
== '\v') {
920 } else if (c
<= 0x1F || c
== 0x7F) {
921 s
<< "\\x" << std::oct
<< std::setw(3)
923 << static_cast<std::uint_least32_t>(c
);
924 } else if (c
< 0x7F) {
926 } else if (c
<= 0xFFFF) {
927 s
<< "\\u" << std::hex
<< std::uppercase
928 << std::setw(4) << std::setfill('0')
929 << static_cast<std::uint_least32_t>(c
);
931 assert(c
<= 0x10FFFF);
932 s
<< "\\U" << std::hex
<< std::uppercase
933 << std::setw(8) << std::setfill('0')
934 << static_cast<std::uint_least32_t>(c
);
938 DiagnosticsEngine::Warning
,
939 ("simplify construction of %0 with UTF-8 content as"
940 " OUString(u\"%1\")"),
942 << classdecl
<< s
.str() << expr
->getSourceRange();
947 if (cont
!= ContentKind::Ascii
|| emb
) {
948 // cf. remaining uses of RTL_CONSTASCII_USTRINGPARAM
951 kind
= ChangeKind::Char
;
953 ? PassThrough::EmptyConstantString
954 : PassThrough::NonEmptyConstantString
;
961 if (!calls_
.empty()) {
962 Expr
const * call
= calls_
.top();
963 CallExpr::const_arg_iterator argsBeg
;
964 CallExpr::const_arg_iterator argsEnd
;
965 if (isa
<CallExpr
>(call
)) {
966 argsBeg
= cast
<CallExpr
>(call
)->arg_begin();
967 argsEnd
= cast
<CallExpr
>(call
)->arg_end();
968 } else if (isa
<CXXConstructExpr
>(call
)) {
969 argsBeg
= cast
<CXXConstructExpr
>(call
)->arg_begin();
970 argsEnd
= cast
<CXXConstructExpr
>(call
)->arg_end();
974 for (auto i(argsBeg
); i
!= argsEnd
; ++i
) {
975 Expr
const * e
= (*i
)->IgnoreParenImpCasts();
976 if (isa
<MaterializeTemporaryExpr
>(e
)) {
977 e
= cast
<MaterializeTemporaryExpr
>(e
)->GetTemporaryExpr()
978 ->IgnoreParenImpCasts();
980 if (isa
<CXXFunctionalCastExpr
>(e
)) {
981 e
= cast
<CXXFunctionalCastExpr
>(e
)->getSubExpr()
982 ->IgnoreParenImpCasts();
984 if (isa
<CXXBindTemporaryExpr
>(e
)) {
985 e
= cast
<CXXBindTemporaryExpr
>(e
)->getSubExpr()
986 ->IgnoreParenImpCasts();
989 if (isa
<CallExpr
>(call
)) {
990 FunctionDecl
const * fdecl
991 = cast
<CallExpr
>(call
)->getDirectCallee();
992 if (fdecl
== nullptr) {
995 loplugin::DeclCheck
dc(fdecl
);
996 if (pass
== PassThrough::EmptyConstantString
) {
997 if ((dc
.Function("equals").Class("OUString")
998 .Namespace("rtl").GlobalNamespace())
999 || (dc
.Operator(OO_EqualEqual
).Namespace("rtl")
1000 .GlobalNamespace()))
1003 DiagnosticsEngine::Warning
,
1004 ("rewrite call of '%0' with construction of"
1005 " %1 with empty string constant argument"
1006 " as call of 'rtl::OUString::isEmpty'"),
1007 getMemberLocation(call
))
1008 << fdecl
->getQualifiedNameAsString()
1009 << classdecl
<< call
->getSourceRange();
1012 if (dc
.Operator(OO_ExclaimEqual
).Namespace("rtl")
1016 DiagnosticsEngine::Warning
,
1017 ("rewrite call of '%0' with construction of"
1018 " %1 with empty string constant argument"
1019 " as call of '!rtl::OUString::isEmpty'"),
1020 getMemberLocation(call
))
1021 << fdecl
->getQualifiedNameAsString()
1022 << classdecl
<< call
->getSourceRange();
1025 if ((dc
.Operator(OO_Plus
).Namespace("rtl")
1027 || (dc
.Operator(OO_Plus
).Class("OUString")
1028 .Namespace("rtl").GlobalNamespace()))
1031 DiagnosticsEngine::Warning
,
1032 ("call of '%0' with suspicious construction"
1033 " of %1 with empty string constant"
1035 getMemberLocation(call
))
1036 << fdecl
->getQualifiedNameAsString()
1037 << classdecl
<< call
->getSourceRange();
1040 if (dc
.Operator(OO_Equal
).Class("OUString")
1041 .Namespace("rtl").GlobalNamespace())
1044 DiagnosticsEngine::Warning
,
1045 ("rewrite call of '%0' with construction of"
1046 " %1 with empty string constant argument"
1047 " as call of 'rtl::OUString::clear'"),
1048 getMemberLocation(call
))
1049 << fdecl
->getQualifiedNameAsString()
1050 << classdecl
<< call
->getSourceRange();
1054 assert(pass
== PassThrough::NonEmptyConstantString
);
1055 if (dc
.Function("equals").Class("OUString")
1056 .Namespace("rtl").GlobalNamespace())
1059 DiagnosticsEngine::Warning
,
1060 ("rewrite call of '%0' with construction of"
1061 " %1 with %2 as 'operator =='"),
1062 getMemberLocation(call
))
1063 << fdecl
->getQualifiedNameAsString()
1064 << classdecl
<< describeChangeKind(kind
)
1065 << call
->getSourceRange();
1068 if ((dc
.Operator(OO_Plus
).Namespace("rtl")
1070 || (dc
.Operator(OO_Plus
).Class("OUString")
1071 .Namespace("rtl").GlobalNamespace())
1072 || (dc
.Operator(OO_EqualEqual
).Namespace("rtl")
1074 || (dc
.Operator(OO_ExclaimEqual
)
1075 .Namespace("rtl").GlobalNamespace()))
1077 if (dc
.Operator(OO_Plus
).Namespace("rtl")
1081 compiler
.getSourceManager().getFilename(
1082 compiler
.getSourceManager()
1084 expr
->getLocStart()));
1085 if (loplugin::isSamePathname(
1088 "/sal/qa/rtl/strings/test_ostring_concat.cxx"))
1089 || loplugin::isSamePathname(
1092 "/sal/qa/rtl/strings/test_oustring_concat.cxx")))
1097 auto loc
= expr
->getArg(0)->getLocStart();
1098 while (compiler
.getSourceManager()
1099 .isMacroArgExpansion(loc
))
1101 loc
= compiler
.getSourceManager()
1102 .getImmediateMacroCallerLoc(loc
);
1104 if ((compiler
.getSourceManager()
1105 .isMacroBodyExpansion(loc
))
1106 && (Lexer::getImmediateMacroName(
1107 loc
, compiler
.getSourceManager(),
1108 compiler
.getLangOpts())
1109 == "OSL_THIS_FUNC"))
1113 if (kind
== ChangeKind::SingleChar
) {
1115 DiagnosticsEngine::Warning
,
1116 ("rewrite construction of %0 with %1 in"
1117 " call of '%2' as construction of"
1118 " 'OUStringLiteral1'"),
1119 getMemberLocation(expr
))
1120 << classdecl
<< describeChangeKind(kind
)
1121 << fdecl
->getQualifiedNameAsString()
1122 << expr
->getSourceRange();
1125 DiagnosticsEngine::Warning
,
1126 ("elide construction of %0 with %1 in"
1128 getMemberLocation(expr
))
1129 << classdecl
<< describeChangeKind(kind
)
1130 << fdecl
->getQualifiedNameAsString()
1131 << expr
->getSourceRange();
1136 } else if (isa
<CXXConstructExpr
>(call
)) {
1145 DiagnosticsEngine::Warning
,
1146 "simplify construction of %0 with %1", expr
->getExprLoc())
1147 << classdecl
<< describeChangeKind(kind
)
1148 << expr
->getSourceRange();
1153 auto consDecl
= expr
->getConstructor();
1154 for (unsigned i
= 0; i
!= consDecl
->getNumParams(); ++i
) {
1155 auto t
= consDecl
->getParamDecl(i
)->getType();
1156 if (loplugin::TypeCheck(t
).NotSubstTemplateTypeParmType()
1157 .LvalueReference().Const().NotSubstTemplateTypeParmType()
1158 .Class("OUString").Namespace("rtl").GlobalNamespace())
1160 auto argExpr
= expr
->getArg(i
);
1161 if (argExpr
&& i
<= consDecl
->getNumParams())
1163 if (!hasOverloads(consDecl
, expr
->getNumArgs()))
1165 handleOUStringCtor(expr
, argExpr
, consDecl
, true);
1174 std::string
StringConstant::describeChangeKind(ChangeKind kind
) {
1176 case ChangeKind::Char
:
1177 return "string constant argument";
1178 case ChangeKind::CharLen
:
1179 return "string constant and matching length arguments";
1180 case ChangeKind::SingleChar
:
1181 return "sal_Unicode argument";
1182 case ChangeKind::OUStringLiteral1
:
1183 return "OUStringLiteral1 argument";
1189 bool StringConstant::isStringConstant(
1190 Expr
const * expr
, unsigned * size
, bool * nonArray
, ContentKind
* content
,
1191 bool * embeddedNuls
, bool * terminatingNul
,
1192 std::vector
<char32_t
> * utf8Content
)
1194 assert(expr
!= nullptr);
1195 assert(size
!= nullptr);
1196 assert(nonArray
!= nullptr);
1197 assert(content
!= nullptr);
1198 assert(embeddedNuls
!= nullptr);
1199 assert(terminatingNul
!= nullptr);
1200 QualType t
= expr
->getType();
1201 // Look inside RTL_CONSTASCII_STRINGPARAM:
1202 if (loplugin::TypeCheck(t
).Pointer().Const().Char()) {
1203 auto e2
= dyn_cast
<UnaryOperator
>(expr
);
1204 if (e2
!= nullptr && e2
->getOpcode() == UO_AddrOf
) {
1205 auto e3
= dyn_cast
<ArraySubscriptExpr
>(
1206 e2
->getSubExpr()->IgnoreParenImpCasts());
1207 if (e3
== nullptr || !isZero(e3
->getIdx()->IgnoreParenImpCasts())) {
1210 expr
= e3
->getBase()->IgnoreParenImpCasts();
1211 t
= expr
->getType();
1214 if (!t
.isConstQualified()) {
1217 DeclRefExpr
const * dre
= dyn_cast
<DeclRefExpr
>(expr
);
1218 if (dre
!= nullptr) {
1219 VarDecl
const * var
= dyn_cast
<VarDecl
>(dre
->getDecl());
1220 if (var
!= nullptr) {
1221 Expr
const * init
= var
->getAnyInitializer();
1222 if (init
!= nullptr) {
1223 expr
= init
->IgnoreParenImpCasts();
1228 if (loplugin::TypeCheck(t
).Pointer().Const().Char()) {
1230 } else if (t
->isConstantArrayType()
1231 && (loplugin::TypeCheck(
1232 t
->getAsArrayTypeUnsafe()->getElementType())
1239 clang::StringLiteral
const * lit
= dyn_cast
<clang::StringLiteral
>(expr
);
1240 if (lit
!= nullptr) {
1241 if (!(lit
->isAscii() || lit
->isUTF8())) {
1244 unsigned n
= lit
->getLength();
1245 ContentKind cont
= ContentKind::Ascii
;
1248 enum class Utf8State
{ Start
, E0
, EB
, F0
, F4
, Trail1
, Trail2
, Trail3
};
1249 Utf8State s
= Utf8State::Start
;
1250 StringRef str
= lit
->getString();
1251 for (unsigned i
= 0; i
!= n
; ++i
) {
1252 auto const c
= static_cast<unsigned char>(str
[i
]);
1257 case Utf8State::Start
:
1259 if (c
>= 0xC2 && c
<= 0xDF) {
1261 s
= Utf8State::Trail1
;
1262 } else if (c
== 0xE0) {
1265 } else if ((c
>= 0xE1 && c
<= 0xEA)
1266 || (c
>= 0xEE && c
<= 0xEF))
1269 s
= Utf8State::Trail2
;
1270 } else if (c
== 0xEB) {
1273 } else if (c
== 0xF0) {
1276 } else if (c
>= 0xF1 && c
<= 0xF3) {
1278 s
= Utf8State::Trail3
;
1279 } else if (c
== 0xF4) {
1283 cont
= ContentKind::Arbitrary
;
1285 } else if (utf8Content
!= nullptr
1286 && cont
!= ContentKind::Arbitrary
)
1288 utf8Content
->push_back(c
);
1292 if (c
>= 0xA0 && c
<= 0xBF) {
1293 val
= (val
<< 6) | (c
& 0x3F);
1294 s
= Utf8State::Trail1
;
1296 cont
= ContentKind::Arbitrary
;
1297 s
= Utf8State::Start
;
1301 if (c
>= 0x80 && c
<= 0x9F) {
1302 val
= (val
<< 6) | (c
& 0x3F);
1303 s
= Utf8State::Trail1
;
1305 cont
= ContentKind::Arbitrary
;
1306 s
= Utf8State::Start
;
1310 if (c
>= 0x90 && c
<= 0xBF) {
1311 val
= (val
<< 6) | (c
& 0x3F);
1312 s
= Utf8State::Trail2
;
1314 cont
= ContentKind::Arbitrary
;
1315 s
= Utf8State::Start
;
1319 if (c
>= 0x80 && c
<= 0x8F) {
1320 val
= (val
<< 6) | (c
& 0x3F);
1321 s
= Utf8State::Trail2
;
1323 cont
= ContentKind::Arbitrary
;
1324 s
= Utf8State::Start
;
1327 case Utf8State::Trail1
:
1328 if (c
>= 0x80 && c
<= 0xBF) {
1329 cont
= ContentKind::Utf8
;
1330 if (utf8Content
!= nullptr)
1332 utf8Content
->push_back((val
<< 6) | (c
& 0x3F));
1336 cont
= ContentKind::Arbitrary
;
1338 s
= Utf8State::Start
;
1340 case Utf8State::Trail2
:
1341 if (c
>= 0x80 && c
<= 0xBF) {
1342 val
= (val
<< 6) | (c
& 0x3F);
1343 s
= Utf8State::Trail1
;
1345 cont
= ContentKind::Arbitrary
;
1346 s
= Utf8State::Start
;
1349 case Utf8State::Trail3
:
1350 if (c
>= 0x80 && c
<= 0xBF) {
1351 val
= (val
<< 6) | (c
& 0x3F);
1352 s
= Utf8State::Trail2
;
1354 cont
= ContentKind::Arbitrary
;
1355 s
= Utf8State::Start
;
1363 *embeddedNuls
= emb
;
1364 *terminatingNul
= true;
1368 if (!expr
->isCXX11ConstantExpr(compiler
.getASTContext(), &v
)) {
1371 switch (v
.getKind()) {
1372 case APValue::LValue
:
1374 Expr
const * e
= v
.getLValueBase().dyn_cast
<Expr
const *>();
1378 if (!v
.getLValueOffset().isZero()) {
1379 return false; //TODO
1381 Expr
const * e2
= e
->IgnoreParenImpCasts();
1383 return isStringConstant(
1384 e2
, size
, nonArray
, content
, embeddedNuls
, terminatingNul
);
1386 //TODO: string literals are represented as recursive LValues???
1388 = compiler
.getASTContext().getAsConstantArrayType(t
)->getSize();
1391 assert(n
.ule(std::numeric_limits
<unsigned>::max()));
1392 *size
= static_cast<unsigned>(n
.getLimitedValue());
1393 *nonArray
= isPtr
|| *nonArray
;
1394 *content
= ContentKind::Ascii
; //TODO
1395 *embeddedNuls
= false; //TODO
1396 *terminatingNul
= true;
1399 case APValue::Array
:
1401 if (v
.hasArrayFiller()) { //TODO: handle final NUL filler?
1404 unsigned n
= v
.getArraySize();
1406 ContentKind cont
= ContentKind::Ascii
;
1408 //TODO: check for ContentType::Utf8
1409 for (unsigned i
= 0; i
!= n
- 1; ++i
) {
1410 APValue
e(v
.getArrayInitializedElt(i
));
1411 if (!e
.isInt()) { //TODO: assert?
1414 APSInt iv
= e
.getInt();
1417 } else if (iv
.uge(0x80)) {
1418 cont
= ContentKind::Arbitrary
;
1421 APValue
e(v
.getArrayInitializedElt(n
- 1));
1422 if (!e
.isInt()) { //TODO: assert?
1425 bool trm
= e
.getInt() == 0;
1426 *size
= trm
? n
- 1 : n
;
1429 *embeddedNuls
= emb
;
1430 *terminatingNul
= trm
;
1434 assert(false); //TODO???
1439 bool StringConstant::isZero(Expr
const * expr
) {
1441 return expr
->EvaluateAsInt(res
, compiler
.getASTContext()) && res
== 0;
1444 void StringConstant::reportChange(
1445 Expr
const * expr
, ChangeKind kind
, std::string
const & original
,
1446 std::string
const & replacement
, PassThrough pass
, bool nonArray
,
1447 char const * rewriteFrom
, char const * rewriteTo
)
1449 assert((rewriteFrom
== nullptr) == (rewriteTo
== nullptr));
1450 if (pass
!= PassThrough::No
&& !calls_
.empty()) {
1451 Expr
const * call
= calls_
.top();
1452 CallExpr::const_arg_iterator argsBeg
;
1453 CallExpr::const_arg_iterator argsEnd
;
1454 if (isa
<CallExpr
>(call
)) {
1455 argsBeg
= cast
<CallExpr
>(call
)->arg_begin();
1456 argsEnd
= cast
<CallExpr
>(call
)->arg_end();
1457 } else if (isa
<CXXConstructExpr
>(call
)) {
1458 argsBeg
= cast
<CXXConstructExpr
>(call
)->arg_begin();
1459 argsEnd
= cast
<CXXConstructExpr
>(call
)->arg_end();
1463 for (auto i(argsBeg
); i
!= argsEnd
; ++i
) {
1464 Expr
const * e
= (*i
)->IgnoreParenImpCasts();
1465 if (isa
<CXXBindTemporaryExpr
>(e
)) {
1466 e
= cast
<CXXBindTemporaryExpr
>(e
)->getSubExpr()
1467 ->IgnoreParenImpCasts();
1470 if (isa
<CallExpr
>(call
)) {
1471 FunctionDecl
const * fdecl
1472 = cast
<CallExpr
>(call
)->getDirectCallee();
1473 if (fdecl
== nullptr) {
1476 loplugin::DeclCheck
dc(fdecl
);
1477 if (pass
== PassThrough::EmptyConstantString
) {
1478 if ((dc
.Function("equals").Class("OUString")
1479 .Namespace("rtl").GlobalNamespace())
1480 || (dc
.Operator(OO_EqualEqual
).Namespace("rtl")
1481 .GlobalNamespace()))
1484 DiagnosticsEngine::Warning
,
1485 ("rewrite call of '%0' with call of %1 with"
1486 " empty string constant argument as call of"
1487 " 'rtl::OUString::isEmpty'"),
1488 getMemberLocation(call
))
1489 << fdecl
->getQualifiedNameAsString() << original
1490 << call
->getSourceRange();
1493 if (dc
.Operator(OO_ExclaimEqual
).Namespace("rtl")
1497 DiagnosticsEngine::Warning
,
1498 ("rewrite call of '%0' with call of %1 with"
1499 " empty string constant argument as call of"
1500 " '!rtl::OUString::isEmpty'"),
1501 getMemberLocation(call
))
1502 << fdecl
->getQualifiedNameAsString() << original
1503 << call
->getSourceRange();
1506 if ((dc
.Operator(OO_Plus
).Namespace("rtl")
1508 || (dc
.Operator(OO_Plus
).Class("OUString")
1509 .Namespace("rtl").GlobalNamespace()))
1512 DiagnosticsEngine::Warning
,
1513 ("call of '%0' with suspicious call of %1 with"
1514 " empty string constant argument"),
1515 getMemberLocation(call
))
1516 << fdecl
->getQualifiedNameAsString() << original
1517 << call
->getSourceRange();
1520 if (dc
.Operator(OO_Equal
).Class("OUString")
1521 .Namespace("rtl").GlobalNamespace())
1524 DiagnosticsEngine::Warning
,
1525 ("rewrite call of '%0' with call of %1 with"
1526 " empty string constant argument as call of"
1527 " rtl::OUString::call"),
1528 getMemberLocation(call
))
1529 << fdecl
->getQualifiedNameAsString() << original
1530 << call
->getSourceRange();
1534 DiagnosticsEngine::Warning
,
1535 "TODO call inside %0", getMemberLocation(expr
))
1536 << fdecl
->getQualifiedNameAsString()
1537 << expr
->getSourceRange();
1540 assert(pass
== PassThrough::NonEmptyConstantString
);
1541 if ((dc
.Function("equals").Class("OUString")
1542 .Namespace("rtl").GlobalNamespace())
1543 || (dc
.Operator(OO_Equal
).Class("OUString")
1544 .Namespace("rtl").GlobalNamespace())
1545 || (dc
.Operator(OO_EqualEqual
).Namespace("rtl")
1547 || (dc
.Operator(OO_ExclaimEqual
).Namespace("rtl")
1548 .GlobalNamespace()))
1551 DiagnosticsEngine::Warning
,
1552 "elide call of %0 with %1 in call of '%2'",
1553 getMemberLocation(expr
))
1554 << original
<< describeChangeKind(kind
)
1555 << fdecl
->getQualifiedNameAsString()
1556 << expr
->getSourceRange();
1560 DiagnosticsEngine::Warning
,
1561 ("rewrite call of %0 with %1 in call of '%2' as"
1562 " (implicit) construction of 'OUString'"),
1563 getMemberLocation(expr
))
1564 << original
<< describeChangeKind(kind
)
1565 << fdecl
->getQualifiedNameAsString()
1566 << expr
->getSourceRange();
1569 } else if (isa
<CXXConstructExpr
>(call
)) {
1570 auto classdecl
= cast
<CXXConstructExpr
>(call
)
1571 ->getConstructor()->getParent();
1572 loplugin::DeclCheck
dc(classdecl
);
1573 if (dc
.Class("OUString").Namespace("rtl").GlobalNamespace()
1574 || (dc
.Class("OUStringBuffer").Namespace("rtl")
1575 .GlobalNamespace()))
1577 //TODO: propagate further out?
1578 if (pass
== PassThrough::EmptyConstantString
) {
1580 DiagnosticsEngine::Warning
,
1581 ("rewrite construction of %0 with call of %1"
1582 " with empty string constant argument as"
1583 " default construction of %0"),
1584 getMemberLocation(call
))
1585 << classdecl
<< original
1586 << call
->getSourceRange();
1588 assert(pass
== PassThrough::NonEmptyConstantString
);
1590 DiagnosticsEngine::Warning
,
1591 ("elide call of %0 with %1 in construction of"
1593 getMemberLocation(expr
))
1594 << original
<< describeChangeKind(kind
)
1595 << classdecl
<< expr
->getSourceRange();
1605 if (rewriter
!= nullptr && !nonArray
&& rewriteFrom
!= nullptr) {
1606 SourceLocation loc
= getMemberLocation(expr
);
1607 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
1608 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
);
1610 if (compiler
.getSourceManager().isMacroBodyExpansion(loc
)) {
1611 loc
= compiler
.getSourceManager().getSpellingLoc(loc
);
1613 unsigned n
= Lexer::MeasureTokenLength(
1614 loc
, compiler
.getSourceManager(), compiler
.getLangOpts());
1615 if ((std::string(compiler
.getSourceManager().getCharacterData(loc
), n
)
1617 && replaceText(loc
, n
, rewriteTo
))
1623 DiagnosticsEngine::Warning
,
1624 "rewrite call of '%0' with %1 as call of '%2'%3",
1625 getMemberLocation(expr
))
1626 << original
<< describeChangeKind(kind
) << replacement
1627 << adviseNonArray(nonArray
) << expr
->getSourceRange();
1630 void StringConstant::checkEmpty(
1631 CallExpr
const * expr
, FunctionDecl
const * callee
, TreatEmpty treatEmpty
,
1632 unsigned size
, std::string
* replacement
)
1634 assert(replacement
!= nullptr);
1636 switch (treatEmpty
) {
1637 case TreatEmpty::DefaultCtor
:
1638 *replacement
= "rtl::OUString default constructor";
1640 case TreatEmpty::CheckEmpty
:
1641 *replacement
= "rtl::OUString::isEmpty";
1643 case TreatEmpty::Error
:
1645 DiagnosticsEngine::Warning
,
1646 "call of '%0' with suspicious empty string constant argument",
1647 getMemberLocation(expr
))
1648 << callee
->getQualifiedNameAsString() << expr
->getSourceRange();
1654 void StringConstant::handleChar(
1655 CallExpr
const * expr
, unsigned arg
, FunctionDecl
const * callee
,
1656 std::string
const & replacement
, TreatEmpty treatEmpty
, bool literal
,
1657 char const * rewriteFrom
, char const * rewriteTo
)
1664 if (!isStringConstant(
1665 expr
->getArg(arg
)->IgnoreParenImpCasts(), &n
, &nonArray
, &cont
,
1670 if (cont
!= ContentKind::Ascii
) {
1672 DiagnosticsEngine::Warning
,
1673 ("call of '%0' with string constant argument containing non-ASCII"
1675 getMemberLocation(expr
))
1676 << callee
->getQualifiedNameAsString() << expr
->getSourceRange();
1681 DiagnosticsEngine::Warning
,
1682 ("call of '%0' with string constant argument containing embedded"
1684 getMemberLocation(expr
))
1685 << callee
->getQualifiedNameAsString() << expr
->getSourceRange();
1690 DiagnosticsEngine::Warning
,
1691 ("call of '%0' with string constant argument lacking a terminating"
1693 getMemberLocation(expr
))
1694 << callee
->getQualifiedNameAsString() << expr
->getSourceRange();
1697 std::string
repl(replacement
);
1698 checkEmpty(expr
, callee
, treatEmpty
, n
, &repl
);
1700 expr
, ChangeKind::Char
, callee
->getQualifiedNameAsString(), repl
,
1703 ? PassThrough::EmptyConstantString
1704 : PassThrough::NonEmptyConstantString
)
1706 nonArray
, rewriteFrom
, rewriteTo
);
1709 void StringConstant::handleCharLen(
1710 CallExpr
const * expr
, unsigned arg1
, unsigned arg2
,
1711 FunctionDecl
const * callee
, std::string
const & replacement
,
1712 TreatEmpty treatEmpty
)
1714 // Especially for f(RTL_CONSTASCII_STRINGPARAM("foo")), where
1715 // RTL_CONSTASCII_STRINGPARAM expands to complicated expressions involving
1716 // (&(X)[0] sub-expressions (and it might or might not be better to handle
1717 // that at the level of non-expanded macros instead, but I have not found
1718 // out how to do that yet anyway):
1724 if (!(isStringConstant(
1725 expr
->getArg(arg1
)->IgnoreParenImpCasts(), &n
, &nonArray
, &cont
,
1732 if (expr
->getArg(arg2
)->EvaluateAsInt(res
, compiler
.getASTContext())) {
1737 UnaryOperator
const * op
= dyn_cast
<UnaryOperator
>(
1738 expr
->getArg(arg1
)->IgnoreParenImpCasts());
1739 if (op
== nullptr || op
->getOpcode() != UO_AddrOf
) {
1742 ArraySubscriptExpr
const * subs
= dyn_cast
<ArraySubscriptExpr
>(
1743 op
->getSubExpr()->IgnoreParenImpCasts());
1744 if (subs
== nullptr) {
1752 if (!(isStringConstant(
1753 subs
->getBase()->IgnoreParenImpCasts(), &n2
, &nonArray2
,
1754 &cont2
, &emb2
, &trm2
)
1755 && n2
== n
&& cont2
== cont
&& emb2
== emb
&& trm2
== trm
1756 //TODO: same strings
1757 && subs
->getIdx()->EvaluateAsInt(res
, compiler
.getASTContext())
1763 if (cont
!= ContentKind::Ascii
) {
1765 DiagnosticsEngine::Warning
,
1766 ("call of '%0' with string constant argument containing non-ASCII"
1768 getMemberLocation(expr
))
1769 << callee
->getQualifiedNameAsString() << expr
->getSourceRange();
1774 std::string
repl(replacement
);
1775 checkEmpty(expr
, callee
, treatEmpty
, n
, &repl
);
1777 expr
, ChangeKind::CharLen
, callee
->getQualifiedNameAsString(), repl
,
1778 PassThrough::No
, nonArray
, nullptr, nullptr);
1781 void StringConstant::handleOUStringCtor(
1782 CallExpr
const * expr
, unsigned arg
, FunctionDecl
const * callee
,
1783 bool explicitFunctionalCastNotation
)
1785 handleOUStringCtor(expr
, expr
->getArg(arg
), callee
, explicitFunctionalCastNotation
);
1788 void StringConstant::handleOUStringCtor(
1789 Expr
const * expr
, Expr
const * argExpr
, FunctionDecl
const * callee
,
1790 bool explicitFunctionalCastNotation
)
1792 auto e0
= argExpr
->IgnoreParenImpCasts();
1793 auto e1
= dyn_cast
<CXXFunctionalCastExpr
>(e0
);
1794 if (e1
== nullptr) {
1795 if (explicitFunctionalCastNotation
) {
1799 e0
= e1
->getSubExpr()->IgnoreParenImpCasts();
1801 auto e2
= dyn_cast
<CXXBindTemporaryExpr
>(e0
);
1802 if (e2
== nullptr) {
1805 auto e3
= dyn_cast
<CXXConstructExpr
>(
1806 e2
->getSubExpr()->IgnoreParenImpCasts());
1807 if (e3
== nullptr) {
1810 if (!loplugin::DeclCheck(e3
->getConstructor()).MemberFunction()
1811 .Class("OUString").Namespace("rtl").GlobalNamespace())
1815 if (e3
->getNumArgs() == 0) {
1817 DiagnosticsEngine::Warning
,
1818 ("in call of '%0', replace default-constructed 'OUString' with an"
1819 " empty string literal"),
1821 << callee
->getQualifiedNameAsString() << expr
->getSourceRange();
1824 if (e3
->getNumArgs() == 1
1825 && e3
->getConstructor()->getNumParams() == 1
1826 && (loplugin::TypeCheck(
1827 e3
->getConstructor()->getParamDecl(0)->getType())
1828 .Typedef("sal_Unicode").GlobalNamespace()))
1830 // It may not be easy to rewrite OUString(c), esp. given there is no
1831 // OUString ctor taking an OUStringLiteral1 arg, so don't warn there:
1832 if (!explicitFunctionalCastNotation
) {
1834 DiagnosticsEngine::Warning
,
1835 ("in call of '%0', replace 'OUString' constructed from a"
1836 " 'sal_Unicode' with an 'OUStringLiteral1'"),
1838 << callee
->getQualifiedNameAsString() << expr
->getSourceRange();
1842 if (e3
->getNumArgs() != 2) {
1850 if (!isStringConstant(
1851 e3
->getArg(0)->IgnoreParenImpCasts(), &n
, &nonArray
, &cont
, &emb
,
1856 //TODO: cont, emb, trm
1857 if (rewriter
!= nullptr) {
1858 auto loc1
= e3
->getLocStart();
1859 auto range
= e3
->getParenOrBraceRange();
1860 if (loc1
.isFileID() && range
.getBegin().isFileID()
1861 && range
.getEnd().isFileID())
1863 auto loc2
= range
.getBegin();
1864 for (bool first
= true;; first
= false) {
1865 unsigned n
= Lexer::MeasureTokenLength(
1866 loc2
, compiler
.getSourceManager(), compiler
.getLangOpts());
1869 compiler
.getSourceManager().getCharacterData(loc2
), n
);
1870 while (s
.startswith("\\\n")) {
1871 s
= s
.drop_front(2);
1873 && (s
.front() == ' ' || s
.front() == '\t'
1874 || s
.front() == '\n' || s
.front() == '\v'
1875 || s
.front() == '\f'))
1877 s
= s
.drop_front(1);
1880 if (!(s
.empty() || s
.startswith("/*") || s
.startswith("//")
1886 loc2
= loc2
.getLocWithOffset(std::max
<unsigned>(n
, 1));
1888 auto loc3
= range
.getEnd();
1890 auto l
= Lexer::GetBeginningOfToken(
1891 loc3
.getLocWithOffset(-1), compiler
.getSourceManager(),
1892 compiler
.getLangOpts());
1893 unsigned n
= Lexer::MeasureTokenLength(
1894 l
, compiler
.getSourceManager(), compiler
.getLangOpts());
1895 StringRef
s(compiler
.getSourceManager().getCharacterData(l
), n
);
1896 while (s
.startswith("\\\n")) {
1897 s
= s
.drop_front(2);
1899 && (s
.front() == ' ' || s
.front() == '\t'
1900 || s
.front() == '\n' || s
.front() == '\v'
1901 || s
.front() == '\f'))
1903 s
= s
.drop_front(1);
1906 if (!(s
.empty() || s
.startswith("/*") || s
.startswith("//")
1913 if (removeText(CharSourceRange(SourceRange(loc1
, loc2
), false))) {
1914 if (removeText(SourceRange(loc3
, range
.getEnd()))) {
1917 report(DiagnosticsEngine::Fatal
, "Corrupt rewrite", loc3
)
1918 << expr
->getSourceRange();
1924 DiagnosticsEngine::Warning
,
1925 ("in call of '%0', replace 'OUString' constructed from a string literal"
1926 " directly with the string literal"),
1928 << callee
->getQualifiedNameAsString() << expr
->getSourceRange();
1931 void StringConstant::handleFunArgOstring(
1932 CallExpr
const * expr
, unsigned arg
, FunctionDecl
const * callee
)
1934 auto argExpr
= expr
->getArg(arg
)->IgnoreParenImpCasts();
1940 if (isStringConstant(argExpr
, &n
, &nonArray
, &cont
, &emb
, &trm
)) {
1941 if (cont
!= ContentKind::Ascii
|| emb
) {
1946 DiagnosticsEngine::Warning
,
1947 ("call of '%0' with string constant argument lacking a"
1948 " terminating NUL"),
1949 getMemberLocation(expr
))
1950 << callee
->getQualifiedNameAsString() << expr
->getSourceRange();
1954 checkEmpty(expr
, callee
, TreatEmpty::Error
, n
, &repl
);
1957 DiagnosticsEngine::Warning
,
1958 ("in call of '%0' with non-array string constant argument,"
1959 " turn the non-array string constant into an array"),
1960 getMemberLocation(expr
))
1961 << callee
->getQualifiedNameAsString() << expr
->getSourceRange();
1963 } else if (auto cexpr
= lookForCXXConstructExpr(argExpr
)) {
1964 auto classdecl
= cexpr
->getConstructor()->getParent();
1965 if (loplugin::DeclCheck(classdecl
).Class("OString").Namespace("rtl")
1968 switch (cexpr
->getConstructor()->getNumParams()) {
1971 DiagnosticsEngine::Warning
,
1972 ("in call of '%0', replace empty %1 constructor with empty"
1974 cexpr
->getLocation())
1975 << callee
->getQualifiedNameAsString() << classdecl
1976 << expr
->getSourceRange();
1979 if (isStringConstant(
1980 cexpr
->getArg(0)->IgnoreParenImpCasts(), &n
, &nonArray
,
1984 if (cexpr
->getArg(1)->EvaluateAsInt(
1985 res
, compiler
.getASTContext()))
1987 if (res
== n
&& !emb
&& trm
) {
1989 DiagnosticsEngine::Warning
,
1990 ("in call of '%0', elide explicit %1"
1992 cexpr
->getLocation())
1993 << callee
->getQualifiedNameAsString()
1994 << classdecl
<< adviseNonArray(nonArray
)
1995 << expr
->getSourceRange();
2000 DiagnosticsEngine::Warning
,
2001 ("call of %0 constructor with string constant"
2002 " argument containing embedded NULs"),
2003 cexpr
->getLocation())
2004 << classdecl
<< cexpr
->getSourceRange();
2009 DiagnosticsEngine::Warning
,
2010 ("call of %0 constructor with string constant"
2011 " argument lacking a terminating NUL"),
2012 cexpr
->getLocation())
2013 << classdecl
<< cexpr
->getSourceRange();
2017 DiagnosticsEngine::Warning
,
2018 "in call of '%0', elide explicit %1 constructor%2",
2019 cexpr
->getLocation())
2020 << callee
->getQualifiedNameAsString() << classdecl
2021 << adviseNonArray(nonArray
)
2022 << expr
->getSourceRange();
2033 loplugin::Plugin::Registration
< StringConstant
> X("stringconstant", true);
2037 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */