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/.
17 // Define a "string constant" to be a constant expression either of type "array
18 // of N char" where each array element is a non-NUL ASCII character---except
19 // that the last array element may be NUL, or, in some situations, of type char
20 // with a ASCII value (including NUL). Note that the former includes
21 // expressions denoting narrow string literals like "foo", and, with toolchains
22 // that support constexpr, constexpr variables declared like
24 // constexpr char str[] = "bar";
26 // This plugin flags uses of OUString functions with string constant arguments
27 // that can be rewritten more directly, like
29 // OUString::createFromAscii("foo") -> "foo"
33 // s.equals(OUString("bar")) -> s == "bar"
37 bool isPlainChar(QualType type
) {
38 return type
->isSpecificBuiltinType(BuiltinType::Char_S
)
39 || type
->isSpecificBuiltinType(BuiltinType::Char_U
);
42 SourceLocation
getMemberLocation(Expr
const * expr
) {
43 CallExpr
const * e1
= dyn_cast
<CallExpr
>(expr
);
44 MemberExpr
const * e2
= e1
== nullptr
45 ? nullptr : dyn_cast
<MemberExpr
>(e1
->getCallee());
46 return e2
== nullptr ? expr
->getExprLoc()/*TODO*/ : e2
->getMemberLoc();
50 public RecursiveASTVisitor
<StringConstant
>, public loplugin::Plugin
53 explicit StringConstant(InstantiationData
const & data
): Plugin(data
) {}
57 bool TraverseCallExpr(CallExpr
* expr
);
59 bool TraverseCXXMemberCallExpr(CXXMemberCallExpr
* expr
);
61 bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr
* expr
);
63 bool TraverseCXXConstructExpr(CXXConstructExpr
* expr
);
65 bool VisitCallExpr(CallExpr
const * expr
);
67 bool VisitCXXConstructExpr(CXXConstructExpr
const * expr
);
70 enum class TreatEmpty
{ DefaultCtor
, CheckEmpty
, Error
};
72 enum class ChangeKind
{ Char
, CharLen
, SingleChar
};
74 enum class PassThrough
{ No
, EmptyConstantString
, NonEmptyConstantString
};
76 std::string
describeChangeKind(ChangeKind kind
);
78 bool isStringConstant(
79 Expr
const * expr
, unsigned * size
, bool * nonAscii
,
80 bool * embeddedNuls
, bool * terminatingNul
);
82 bool isZero(Expr
const * expr
);
85 Expr
const * expr
, ChangeKind kind
, std::string
const & original
,
86 std::string
const & replacement
, PassThrough pass
);
89 CallExpr
const * expr
, std::string
const & qname
, TreatEmpty treatEmpty
,
90 unsigned size
, std::string
* replacement
);
93 CallExpr
const * expr
, unsigned arg
, std::string
const & qname
,
94 std::string
const & replacement
, TreatEmpty treatEmpty
, bool literal
);
97 CallExpr
const * expr
, unsigned arg1
, unsigned arg2
,
98 std::string
const & qname
, std::string
const & replacement
,
99 TreatEmpty treatEmpty
);
101 std::stack
<Expr
const *> calls_
;
104 void StringConstant::run() {
105 if (compiler
.getLangOpts().CPlusPlus
106 && compiler
.getPreprocessor().getIdentifierInfo(
107 "LIBO_INTERNAL_ONLY")->hasMacroDefinition())
108 //TODO: some parts of it are useful for external code, too
110 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
114 bool StringConstant::TraverseCallExpr(CallExpr
* expr
) {
115 if (!WalkUpFromCallExpr(expr
)) {
120 for (auto * e
: expr
->children()) {
121 if (!TraverseStmt(e
)) {
130 bool StringConstant::TraverseCXXMemberCallExpr(CXXMemberCallExpr
* expr
) {
131 if (!WalkUpFromCXXMemberCallExpr(expr
)) {
136 for (auto * e
: expr
->children()) {
137 if (!TraverseStmt(e
)) {
146 bool StringConstant::TraverseCXXOperatorCallExpr(CXXOperatorCallExpr
* expr
)
148 if (!WalkUpFromCXXOperatorCallExpr(expr
)) {
153 for (auto * e
: expr
->children()) {
154 if (!TraverseStmt(e
)) {
163 bool StringConstant::TraverseCXXConstructExpr(CXXConstructExpr
* expr
) {
164 if (!WalkUpFromCXXConstructExpr(expr
)) {
169 for (auto * e
: expr
->children()) {
170 if (!TraverseStmt(e
)) {
179 bool StringConstant::VisitCallExpr(CallExpr
const * expr
) {
180 if (ignoreLocation(expr
)) {
183 FunctionDecl
const * fdecl
= expr
->getDirectCallee();
184 if (fdecl
== nullptr) {
187 std::string
qname(fdecl
->getQualifiedNameAsString());
188 //TODO: u.compareToAscii("foo") -> u.???("foo")
189 //TODO: u.compareToIgnoreAsciiCaseAscii("foo") -> u.???("foo")
190 if (qname
== "rtl::OUString::createFromAscii" && fdecl
->getNumParams() == 1)
192 // OUString::createFromAscii("foo") -> OUString("foo")
194 expr
, 0, qname
, "rtl::OUString constructor",
195 TreatEmpty::DefaultCtor
, true);
198 if (qname
== "rtl::OUString::endsWithAsciiL" && fdecl
->getNumParams() == 2)
200 // u.endsWithAsciiL("foo", 3) -> u.endsWith("foo"):
202 expr
, 0, 1, qname
, "rtl::OUString::endsWith", TreatEmpty::Error
);
205 if (qname
== "rtl::OUString::endsWithIgnoreAsciiCaseAsciiL"
206 && fdecl
->getNumParams() == 2)
208 // u.endsWithIgnoreAsciiCaseAsciiL("foo", 3) ->
209 // u.endsWithIgnoreAsciiCase("foo"):
211 expr
, 0, 1, qname
, "rtl::OUString::endsWithIgnoreAsciiCase",
215 if (qname
== "rtl::OUString::equalsAscii" && fdecl
->getNumParams() == 1) {
216 // u.equalsAscii("foo") -> u == "foo":
218 expr
, 0, qname
, "operator ==", TreatEmpty::CheckEmpty
, false);
221 if (qname
== "rtl::OUString::equalsAsciiL" && fdecl
->getNumParams() == 2) {
222 // u.equalsAsciiL("foo", 3) -> u == "foo":
223 handleCharLen(expr
, 0, 1, qname
, "operator ==", TreatEmpty::CheckEmpty
);
226 if (qname
== "rtl::OUString::equalsIgnoreAsciiCaseAscii"
227 && fdecl
->getNumParams() == 1)
229 // u.equalsIgnoreAsciiCaseAscii("foo") ->
230 // u.equalsIngoreAsciiCase("foo"):
232 expr
, 0, qname
, "rtl::OUString::equalsIgnoreAsciiCase",
233 TreatEmpty::CheckEmpty
, false);
236 if (qname
== "rtl::OUString::equalsIgnoreAsciiCaseAsciiL"
237 && fdecl
->getNumParams() == 2)
239 // u.equalsIgnoreAsciiCaseAsciiL("foo", 3) ->
240 // u.equalsIngoreAsciiCase("foo"):
242 expr
, 0, 1, qname
, "rtl::OUString::equalsIgnoreAsciiCase",
243 TreatEmpty::CheckEmpty
);
246 if (qname
== "rtl::OUString::indexOfAsciiL" && fdecl
->getNumParams() == 3) {
247 assert(expr
->getNumArgs() == 3);
248 // u.indexOfAsciiL("foo", 3, i) -> u.indexOf("foo", i):
250 expr
, 0, 1, qname
, "rtl::OUString::indexOf", TreatEmpty::Error
);
253 if (qname
== "rtl::OUString::lastIndexOfAsciiL"
254 && fdecl
->getNumParams() == 2)
256 // u.lastIndexOfAsciiL("foo", 3) -> u.lastIndexOf("foo"):
258 expr
, 0, 1, qname
, "rtl::OUString::lastIndexOf", TreatEmpty::Error
);
261 if (qname
== "rtl::OUString::matchAsciiL" && fdecl
->getNumParams() == 3) {
262 assert(expr
->getNumArgs() == 3);
263 // u.matchAsciiL("foo", 3, i) -> u.match("foo", i):
266 (isZero(expr
->getArg(2))
267 ? std::string("rtl::OUString::startsWith")
268 : std::string("rtl::OUString::match")),
272 if (qname
== "rtl::OUString::matchIgnoreAsciiCaseAsciiL"
273 && fdecl
->getNumParams() == 3)
275 assert(expr
->getNumArgs() == 3);
276 // u.matchIgnoreAsciiCaseAsciiL("foo", 3, i) ->
277 // u.matchIgnoreAsciiCase("foo", i):
280 (isZero(expr
->getArg(2))
281 ? std::string("rtl::OUString::startsWithIgnoreAsciiCase")
282 : std::string("rtl::OUString::matchIgnoreAsciiCase")),
286 if (qname
== "rtl::OUString::reverseCompareToAsciiL"
287 && fdecl
->getNumParams() == 2)
289 // u.reverseCompareToAsciiL("foo", 3) -> u.reverseCompareTo("foo"):
291 expr
, 0, 1, qname
, "rtl::OUString::reverseCompareTo",
295 if (qname
== "rtl::OUString::equals" && fdecl
->getNumParams() == 1) {
300 if (!isStringConstant(
301 expr
->getArg(0)->IgnoreParenImpCasts(), &n
, &non
, &emb
, &trm
))
307 DiagnosticsEngine::Warning
,
309 + (" with string constant argument containging non-ASCII"
312 << expr
->getSourceRange();
316 DiagnosticsEngine::Warning
,
318 + " with string constant argument containging embedded NULs"),
320 << expr
->getSourceRange();
324 DiagnosticsEngine::Warning
,
325 ("rewrite call of " + qname
326 + (" with empty string constant argument as call of"
327 " rtl::OUString::isEmpty")),
329 << expr
->getSourceRange();
333 if (qname
== "rtl::operator==" && fdecl
->getNumParams() == 2) {
334 for (unsigned i
= 0; i
!= 2; ++i
) {
339 if (!isStringConstant(
340 expr
->getArg(i
)->IgnoreParenImpCasts(), &n
, &non
, &emb
,
347 DiagnosticsEngine::Warning
,
349 + (" with string constant argument containging non-ASCII"
352 << expr
->getSourceRange();
356 DiagnosticsEngine::Warning
,
358 + (" with string constant argument containging embedded"
361 << expr
->getSourceRange();
365 DiagnosticsEngine::Warning
,
366 ("rewrite call of " + qname
367 + (" with empty string constant argument as call of"
368 " rtl::OUString::isEmpty")),
370 << expr
->getSourceRange();
375 if (qname
== "rtl::operator!=" && fdecl
->getNumParams() == 2) {
376 for (unsigned i
= 0; i
!= 2; ++i
) {
381 if (!isStringConstant(
382 expr
->getArg(i
)->IgnoreParenImpCasts(), &n
, &non
, &emb
,
389 DiagnosticsEngine::Warning
,
391 + (" with string constant argument containging non-ASCII"
394 << expr
->getSourceRange();
398 DiagnosticsEngine::Warning
,
400 + (" with string constant argument containging embedded"
403 << expr
->getSourceRange();
407 DiagnosticsEngine::Warning
,
408 ("rewrite call of " + qname
409 + (" with empty string constant argument as call of"
410 " !rtl::OUString::isEmpty")),
412 << expr
->getSourceRange();
417 if (qname
== "rtl::OUString::operator=" && fdecl
->getNumParams() == 1) {
422 if (!isStringConstant(
423 expr
->getArg(1)->IgnoreParenImpCasts(), &n
, &non
, &emb
, &trm
))
429 DiagnosticsEngine::Warning
,
431 + (" with string constant argument containging non-ASCII"
434 << expr
->getSourceRange();
438 DiagnosticsEngine::Warning
,
440 + " with string constant argument containging embedded NULs"),
442 << expr
->getSourceRange();
446 DiagnosticsEngine::Warning
,
447 ("rewrite call of " + qname
448 + (" with empty string constant argument as call of"
449 " rtl::OUString::clear")),
451 << expr
->getSourceRange();
458 bool StringConstant::VisitCXXConstructExpr(CXXConstructExpr
const * expr
) {
459 if (ignoreLocation(expr
)) {
463 expr
->getConstructor()->getParent()->getQualifiedNameAsString());
464 if (qname
== "rtl::OUString") {
467 switch (expr
->getConstructor()->getNumParams()) {
471 if (!expr
->getArg(0)->isIntegerConstantExpr(
472 v
, compiler
.getASTContext()))
476 if (v
== 0 || v
.uge(0x80)) {
479 kind
= ChangeKind::SingleChar
;
480 pass
= PassThrough::NonEmptyConstantString
;
489 if (!isStringConstant(
490 expr
->getArg(0)->IgnoreParenImpCasts(), &n
, &non
, &emb
,
497 DiagnosticsEngine::Warning
,
498 ("construction of " + qname
499 + (" with string constant argument containging"
500 " non-ASCII characters")),
502 << expr
->getSourceRange();
506 DiagnosticsEngine::Warning
,
507 ("construction of " + qname
508 + (" with string constant argument containging"
511 << expr
->getSourceRange();
513 kind
= ChangeKind::Char
;
515 ? PassThrough::EmptyConstantString
516 : PassThrough::NonEmptyConstantString
;
522 if (!calls_
.empty()) {
523 Expr
const * call
= calls_
.top();
524 CallExpr::const_arg_iterator argsBeg
;
525 CallExpr::const_arg_iterator argsEnd
;
526 if (isa
<CallExpr
>(call
)) {
527 argsBeg
= cast
<CallExpr
>(call
)->arg_begin();
528 argsEnd
= cast
<CallExpr
>(call
)->arg_end();
529 } else if (isa
<CXXConstructExpr
>(call
)) {
530 argsBeg
= cast
<CXXConstructExpr
>(call
)->arg_begin();
531 argsEnd
= cast
<CXXConstructExpr
>(call
)->arg_end();
535 for (auto i(argsBeg
); i
!= argsEnd
; ++i
) {
536 Expr
const * e
= (*i
)->IgnoreParenImpCasts();
537 if (isa
<MaterializeTemporaryExpr
>(e
)) {
538 e
= cast
<MaterializeTemporaryExpr
>(e
)->GetTemporaryExpr()
539 ->IgnoreParenImpCasts();
541 if (isa
<CXXFunctionalCastExpr
>(e
)) {
542 e
= cast
<CXXFunctionalCastExpr
>(e
)->getSubExpr()
543 ->IgnoreParenImpCasts();
545 if (isa
<CXXBindTemporaryExpr
>(e
)) {
546 e
= cast
<CXXBindTemporaryExpr
>(e
)->getSubExpr()
547 ->IgnoreParenImpCasts();
550 if (isa
<CallExpr
>(call
)) {
551 FunctionDecl
const * fdecl
552 = cast
<CallExpr
>(call
)->getDirectCallee();
553 if (fdecl
== nullptr) {
556 std::string
callQname(
557 fdecl
->getQualifiedNameAsString());
558 if (pass
== PassThrough::EmptyConstantString
) {
559 if (callQname
== "rtl::OUString::equals"
560 || callQname
== "rtl::operator==")
563 DiagnosticsEngine::Warning
,
564 ("rewrite call of " + callQname
565 + " with construction of " + qname
566 + (" with empty string constant argument"
567 " as call of rtl::OUString::isEmpty")),
568 getMemberLocation(call
))
569 << call
->getSourceRange();
572 if (callQname
== "rtl::operator!=") {
574 DiagnosticsEngine::Warning
,
575 ("rewrite call of " + callQname
576 + " with construction of " + qname
577 + (" with empty string constant argument"
578 " as call of !rtl::OUString::isEmpty")),
579 getMemberLocation(call
))
580 << call
->getSourceRange();
583 if (callQname
== "rtl::operator+"
584 || callQname
== "rtl::OUString::operator+=")
587 DiagnosticsEngine::Warning
,
588 ("call of " + callQname
589 + " with suspicous construction of "
591 + " with empty string constant argument"),
592 getMemberLocation(call
))
593 << call
->getSourceRange();
596 if (callQname
== "rtl::OUString::operator=") {
598 DiagnosticsEngine::Warning
,
599 ("rewrite call of " + callQname
600 + " with construction of " + qname
601 + (" with empty string constant argument"
602 " as call of rtl::OUString::clear")),
603 getMemberLocation(call
))
604 << call
->getSourceRange();
608 assert(pass
== PassThrough::NonEmptyConstantString
);
609 if (callQname
== "rtl::OUString::equals") {
611 DiagnosticsEngine::Warning
,
612 ("rewrite call of " + callQname
613 + " with construction of " + qname
614 + " with " + describeChangeKind(kind
)
615 + " as operator =="),
616 getMemberLocation(call
))
617 << call
->getSourceRange();
620 if (callQname
== "rtl::operator+"
621 || callQname
== "rtl::OUString::operator="
622 || callQname
== "rtl::operator=="
623 || callQname
== "rtl::operator!=")
625 if (callQname
== "rtl::operator+") {
627 compiler
.getSourceManager().getFilename(
628 compiler
.getSourceManager()
630 expr
->getLocStart())));
633 "/sal/qa/rtl/strings/test_ostring_concat.cxx")
636 "/sal/qa/rtl/strings/test_oustring_concat.cxx")))
642 DiagnosticsEngine::Warning
,
643 ("elide construction of " + qname
+ " with "
644 + describeChangeKind(kind
) + " in call of "
646 getMemberLocation(expr
))
647 << expr
->getSourceRange();
652 } else if (isa
<CXXConstructExpr
>(call
)) {
664 std::string
StringConstant::describeChangeKind(ChangeKind kind
) {
666 case ChangeKind::Char
:
667 return "string constant argument";
668 case ChangeKind::CharLen
:
669 return "string constant and matching length arguments";
670 case ChangeKind::SingleChar
:
671 return "ASCII sal_Unicode argument";
675 bool StringConstant::isStringConstant(
676 Expr
const * expr
, unsigned * size
, bool * nonAscii
, bool * embeddedNuls
,
677 bool * terminatingNul
)
679 assert(expr
!= nullptr);
680 assert(size
!= nullptr);
681 assert(nonAscii
!= nullptr);
682 assert(embeddedNuls
!= nullptr);
683 assert(terminatingNul
!= nullptr);
684 QualType t
= expr
->getType();
685 if (!(t
->isConstantArrayType() && t
.isConstQualified()
686 && isPlainChar(t
->getAsArrayTypeUnsafe()->getElementType())))
690 StringLiteral
const * lit
= dyn_cast
<StringLiteral
>(expr
);
691 if (lit
!= nullptr) {
692 if (!lit
->isAscii()) {
695 unsigned n
= lit
->getLength();
698 StringRef str
= lit
->getString();
699 for (unsigned i
= 0; i
!= n
; ++i
) {
700 if (str
[i
] == '\0') {
702 } else if (static_cast<unsigned char>(str
[i
]) >= 0x80) {
709 *terminatingNul
= true;
713 if (!expr
->isCXX11ConstantExpr(compiler
.getASTContext(), &v
)) {
716 switch (v
.getKind()) {
717 case APValue::LValue
:
719 Expr
const * e
= v
.getLValueBase().dyn_cast
<Expr
const *>();
720 assert(e
!= nullptr); //TODO???
721 if (!v
.getLValueOffset().isZero()) {
724 Expr
const * e2
= e
->IgnoreParenImpCasts();
726 return isStringConstant(
727 e2
, size
, nonAscii
, embeddedNuls
, terminatingNul
);
729 //TODO: string literals are represented as recursive LValues???
731 = compiler
.getASTContext().getAsConstantArrayType(t
)->getSize();
734 assert(n
.ule(std::numeric_limits
<unsigned>::max()));
735 *size
= static_cast<unsigned>(n
.getLimitedValue());
736 *nonAscii
= false; //TODO
737 *embeddedNuls
= false; //TODO
738 *terminatingNul
= true;
743 if (v
.hasArrayFiller()) { //TODO: handle final NUL filler?
746 unsigned n
= v
.getArraySize();
750 for (unsigned i
= 0; i
!= n
- 1; ++i
) {
751 APValue
e(v
.getArrayInitializedElt(i
));
752 if (!e
.isInt()) { //TODO: assert?
755 APSInt iv
= e
.getInt();
758 } else if (iv
.uge(0x80)) {
762 APValue
e(v
.getArrayInitializedElt(n
- 1));
763 if (!e
.isInt()) { //TODO: assert?
766 bool trm
= e
.getInt() == 0;
767 *size
= trm
? n
- 1 : n
;
770 *terminatingNul
= trm
;
774 assert(false); //TODO???
779 bool StringConstant::isZero(Expr
const * expr
) {
781 return expr
->isIntegerConstantExpr(res
, compiler
.getASTContext())
785 void StringConstant::reportChange(
786 Expr
const * expr
, ChangeKind kind
, std::string
const & original
,
787 std::string
const & replacement
, PassThrough pass
)
789 if (pass
!= PassThrough::No
&& !calls_
.empty()) {
790 Expr
const * call
= calls_
.top();
791 CallExpr::const_arg_iterator argsBeg
;
792 CallExpr::const_arg_iterator argsEnd
;
793 if (isa
<CallExpr
>(call
)) {
794 argsBeg
= cast
<CallExpr
>(call
)->arg_begin();
795 argsEnd
= cast
<CallExpr
>(call
)->arg_end();
796 } else if (isa
<CXXConstructExpr
>(call
)) {
797 argsBeg
= cast
<CXXConstructExpr
>(call
)->arg_begin();
798 argsEnd
= cast
<CXXConstructExpr
>(call
)->arg_end();
802 for (auto i(argsBeg
); i
!= argsEnd
; ++i
) {
803 Expr
const * e
= (*i
)->IgnoreParenImpCasts();
804 if (isa
<CXXBindTemporaryExpr
>(e
)) {
805 e
= cast
<CXXBindTemporaryExpr
>(e
)->getSubExpr()
806 ->IgnoreParenImpCasts();
809 if (isa
<CallExpr
>(call
)) {
810 FunctionDecl
const * fdecl
811 = cast
<CallExpr
>(call
)->getDirectCallee();
812 if (fdecl
== nullptr) {
815 std::string
qname(fdecl
->getQualifiedNameAsString());
816 if (pass
== PassThrough::EmptyConstantString
) {
817 if (qname
== "rtl::OUString::equals"
818 || qname
== "rtl::operator==")
821 DiagnosticsEngine::Warning
,
822 ("rewrite call of " + qname
+ " with call of "
824 + (" with empty string constant argument as"
825 " call of rtl::OUString::isEmpty")),
826 getMemberLocation(call
))
827 << call
->getSourceRange();
830 if (qname
== "rtl::operator!=") {
832 DiagnosticsEngine::Warning
,
833 ("rewrite call of " + qname
+ " with call of "
835 + (" with empty string constant argument as"
836 " call of !rtl::OUString::isEmpty")),
837 getMemberLocation(call
))
838 << call
->getSourceRange();
841 if (qname
== "rtl::operator+"
842 || qname
== "rtl::OUString::operator+=")
845 DiagnosticsEngine::Warning
,
846 ("call of " + qname
+ " with suspicous call of "
848 + " with empty string constant argument"),
849 getMemberLocation(call
))
850 << call
->getSourceRange();
853 if (qname
== "rtl::OUString::operator=") {
855 DiagnosticsEngine::Warning
,
856 ("rewrite call of " + qname
+ " with call of "
858 + (" with empty string constant argument as"
859 " call of rtl::OUString::call")),
860 getMemberLocation(call
))
861 << call
->getSourceRange();
865 assert(pass
== PassThrough::NonEmptyConstantString
);
866 if (qname
== "rtl::OUString::equals"
867 || qname
== "rtl::OUString::operator="
868 || qname
== "rtl::operator=="
869 || qname
== "rtl::operator!=")
872 DiagnosticsEngine::Warning
,
873 ("elide call of " + original
+ " with "
874 + describeChangeKind(kind
) + " in call of "
876 getMemberLocation(expr
))
877 << expr
->getSourceRange();
880 if (qname
== "rtl::operator+"
881 || qname
== "rtl::OUString::operator+=")
884 DiagnosticsEngine::Warning
,
885 ("rewrite call of " + original
+ " with "
886 + describeChangeKind(kind
) + " in call of "
888 + (" as (implicit) construction of"
890 getMemberLocation(expr
))
891 << expr
->getSourceRange();
896 DiagnosticsEngine::Warning
,
897 "TODO call inside " + qname
, getMemberLocation(expr
))
898 << expr
->getSourceRange();
900 } else if (isa
<CXXConstructExpr
>(call
)) {
902 cast
<CXXConstructExpr
>(call
)->getConstructor()
903 ->getParent()->getQualifiedNameAsString());
904 if (qname
== "rtl::OUString"
905 || qname
== "rtl::OUStringBuffer")
907 //TODO: propagate further out?
908 if (pass
== PassThrough::EmptyConstantString
) {
910 DiagnosticsEngine::Warning
,
911 ("rewrite construction of " + qname
912 + " with call of " + original
913 + (" with empty string constant argument as"
914 " default construction of ")
916 getMemberLocation(call
))
917 << call
->getSourceRange();
919 assert(pass
== PassThrough::NonEmptyConstantString
);
921 DiagnosticsEngine::Warning
,
922 ("elide call of " + original
+ " with "
923 + describeChangeKind(kind
)
924 + " in construction of " + qname
),
925 getMemberLocation(expr
))
926 << expr
->getSourceRange();
937 DiagnosticsEngine::Warning
,
938 ("rewrite call of " + original
+ " with " + describeChangeKind(kind
)
939 + " as call of " + replacement
),
940 getMemberLocation(expr
))
941 << expr
->getSourceRange();
944 void StringConstant::checkEmpty(
945 CallExpr
const * expr
, std::string
const & qname
, TreatEmpty treatEmpty
,
946 unsigned size
, std::string
* replacement
)
948 assert(replacement
!= nullptr);
950 switch (treatEmpty
) {
951 case TreatEmpty::DefaultCtor
:
952 *replacement
= "rtl::OUString default constructor";
954 case TreatEmpty::CheckEmpty
:
955 *replacement
= "rtl::OUString::isEmpty";
957 case TreatEmpty::Error
:
959 DiagnosticsEngine::Warning
,
961 + " with suspicous empty string constant argument"),
962 getMemberLocation(expr
))
963 << expr
->getSourceRange();
969 void StringConstant::handleChar(
970 CallExpr
const * expr
, unsigned arg
, std::string
const & qname
,
971 std::string
const & replacement
, TreatEmpty treatEmpty
, bool literal
)
977 if (!isStringConstant(
978 expr
->getArg(arg
)->IgnoreParenImpCasts(), &n
, &non
, &emb
, &trm
))
984 DiagnosticsEngine::Warning
,
986 + (" with string constant argument containging non-ASCII"
988 getMemberLocation(expr
))
989 << expr
->getSourceRange();
994 DiagnosticsEngine::Warning
,
996 + " with string constant argument containging embedded NULs"),
997 getMemberLocation(expr
))
998 << expr
->getSourceRange();
1003 DiagnosticsEngine::Warning
,
1005 + " with string constant argument lacking a terminating NUL"),
1006 getMemberLocation(expr
))
1007 << expr
->getSourceRange();
1010 std::string
repl(replacement
);
1011 checkEmpty(expr
, qname
, treatEmpty
, n
, &repl
);
1013 expr
, ChangeKind::Char
, qname
, repl
,
1016 ? PassThrough::EmptyConstantString
1017 : PassThrough::NonEmptyConstantString
)
1018 : PassThrough::No
));
1021 void StringConstant::handleCharLen(
1022 CallExpr
const * expr
, unsigned arg1
, unsigned arg2
,
1023 std::string
const & qname
, std::string
const & replacement
,
1024 TreatEmpty treatEmpty
)
1026 // Especially for f(RTL_CONSTASCII_STRINGPARAM("foo")), where
1027 // RTL_CONSTASCII_STRINGPARAM expands to complicated expressions involving
1028 // (&(X)[0] sub-expressions (and it might or might not be better to handle
1029 // that at the level of non-expanded macros instead, but I have not found
1030 // out how to do that yet anyway):
1035 if (!(isStringConstant(
1036 expr
->getArg(arg1
)->IgnoreParenImpCasts(), &n
, &non
, &emb
, &trm
)
1042 if (expr
->getArg(arg2
)->isIntegerConstantExpr(
1043 res
, compiler
.getASTContext()))
1049 UnaryOperator
const * op
= dyn_cast
<UnaryOperator
>(
1050 expr
->getArg(arg1
)->IgnoreParenImpCasts());
1051 if (op
== nullptr || op
->getOpcode() != UO_AddrOf
) {
1054 ArraySubscriptExpr
const * subs
= dyn_cast
<ArraySubscriptExpr
>(
1055 op
->getSubExpr()->IgnoreParenImpCasts());
1056 if (subs
== nullptr) {
1063 if (!(isStringConstant(
1064 subs
->getBase()->IgnoreParenImpCasts(), &n2
, &non2
, &emb2
,
1066 && n2
== n
&& non2
== non
&& emb2
== emb
&& trm2
== trm
1067 //TODO: same strings
1068 && subs
->getIdx()->isIntegerConstantExpr(
1069 res
, compiler
.getASTContext())
1077 DiagnosticsEngine::Warning
,
1079 + (" with string constant argument containging non-ASCII"
1081 getMemberLocation(expr
))
1082 << expr
->getSourceRange();
1087 std::string
repl(replacement
);
1088 checkEmpty(expr
, qname
, treatEmpty
, n
, &repl
);
1089 reportChange(expr
, ChangeKind::CharLen
, qname
, repl
, PassThrough::No
);
1092 loplugin::Plugin::Registration
< StringConstant
> X("stringconstant");
1096 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */