1 /*
2 This file is part of the clazy static checker.
3
4 Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
5 Author: Sérgio Martins <sergio.martins@kdab.com>
6
7 Copyright (C) 2015 Sergio Martins <smartins@kde.org>
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
18
19 You should have received a copy of the GNU Library General Public License
20 along with this library; see the file COPYING.LIB. If not, write to
21 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA.
23 */
24
25 #include "qstring-allocations.h"
26 #include "ClazyContext.h"
27 #include "Utils.h"
28 #include "clazy_stl.h"
29 #include "StringUtils.h"
30 #include "FixItUtils.h"
31 #include "FunctionUtils.h"
32 #include "QtUtils.h"
33 #include "HierarchyUtils.h"
34 #include "SourceCompatibilityHelpers.h"
35
36 #include <clang/AST/DeclCXX.h>
37 #include <clang/AST/ExprCXX.h>
38 #include <clang/AST/Expr.h>
39 #include <clang/Basic/Diagnostic.h>
40 #include <clang/Lex/Lexer.h>
41 #include <clang/AST/Decl.h>
42 #include <clang/AST/Stmt.h>
43 #include <clang/AST/StmtIterator.h>
44 #include <clang/Basic/LLVM.h>
45 #include <clang/Basic/SourceLocation.h>
46 #include <clang/Frontend/CompilerInstance.h>
47 #include <llvm/ADT/StringRef.h>
48 #include <llvm/Support/Casting.h>
49 #include <llvm/Support/raw_ostream.h>
50
51 #include <assert.h>
52
53 namespace clang {
54 class LangOptions;
55 class ParentMap;
56 class SourceManager;
57 } // namespace clang
58
59 /// WARNING
60 ///
61 /// This code is a bit unreadable and unmaintanable due to the fact that there are more corner-cases than normal cases.
62 /// It will be rewritten in a new check, so don't bother.
63
64 using namespace clang;
65 using namespace std;
66
67 enum Fixit {
68 FixitNone = 0,
69 QLatin1StringAllocations = 0x1,
70 FromLatin1_FromUtf8Allocations = 0x2,
71 CharPtrAllocations = 0x4,
72 };
73
74 struct Latin1Expr {
75 CXXConstructExpr *qlatin1ctorexpr;
76 bool enableFixit;
isValidLatin1Expr77 bool isValid() const { return qlatin1ctorexpr != nullptr; }
78 };
79
QStringAllocations(const std::string & name,ClazyContext * context)80 QStringAllocations::QStringAllocations(const std::string &name, ClazyContext *context)
81 : CheckBase(name, context, Option_CanIgnoreIncludes)
82 {
83 }
84
VisitStmt(clang::Stmt * stm)85 void QStringAllocations::VisitStmt(clang::Stmt *stm)
86 {
87 if (m_context->isQtDeveloper() && clazy::isBootstrapping(m_context->ci.getPreprocessorOpts())) {
88 // During bootstrap many QString::fromLatin1() are used instead of tr(), which causes
89 // much noise
90 return;
91 }
92
93 VisitCtor(stm);
94 VisitOperatorCall(stm);
95 VisitFromLatin1OrUtf8(stm);
96 VisitAssignOperatorQLatin1String(stm);
97 }
98
betterTakeQLatin1String(CXXMethodDecl * method,StringLiteral * lt)99 static bool betterTakeQLatin1String(CXXMethodDecl *method, StringLiteral *lt)
100 {
101 static const vector<StringRef> methods = {"append", "compare", "endsWith", "startsWith", "insert",
102 "lastIndexOf", "prepend", "replace", "contains", "indexOf" };
103
104 if (!clazy::isOfClass(method, "QString"))
105 return false;
106
107 return (!lt || Utils::isAscii(lt)) && clazy::contains(methods, clazy::name(method));
108 }
109
110 // Returns the first occurrence of a QLatin1String(char*) CTOR call
qlatin1CtorExpr(Stmt * stm,ConditionalOperator * & ternary)111 Latin1Expr QStringAllocations::qlatin1CtorExpr(Stmt *stm, ConditionalOperator * &ternary)
112 {
113 if (!stm)
114 return {};
115
116 auto constructExpr = dyn_cast<CXXConstructExpr>(stm);
117 if (constructExpr) {
118 CXXConstructorDecl *ctor = constructExpr->getConstructor();
119 const int numArgs = ctor->getNumParams();
120 if (clazy::isOfClass(ctor, "QLatin1String")) {
121
122 if (Utils::containsStringLiteral(constructExpr, /*allowEmpty=*/ false, 2))
123 return {constructExpr, /*enableFixits=*/ numArgs == 1};
124
125 if (Utils::userDefinedLiteral(constructExpr, "QLatin1String", lo()))
126 return {constructExpr, /*enableFixits=*/ false};
127 }
128 }
129
130 if (!ternary)
131 ternary = dyn_cast<ConditionalOperator>(stm);
132
133 for (auto child : stm->children()) {
134 auto expr = qlatin1CtorExpr(child, ternary);
135 if (expr.isValid())
136 return expr;
137 }
138
139 return {};
140 }
141
142 // Returns true if there's a literal in the hierarchy, but aborts if it's parented on CallExpr
143 // so, returns true for: QLatin1String("foo") but false for QLatin1String(indirection("foo"));
144 //
containsStringLiteralNoCallExpr(Stmt * stmt)145 static bool containsStringLiteralNoCallExpr(Stmt *stmt)
146 {
147 if (!stmt)
148 return false;
149
150 StringLiteral *sl = dyn_cast<StringLiteral>(stmt);
151 if (sl)
152 return true;
153
154 for (auto child : stmt->children()) {
155 if (!child)
156 continue;
157 CallExpr *callExpr = dyn_cast<CallExpr>(child);
158 if (!callExpr && containsStringLiteralNoCallExpr(child))
159 return true;
160 }
161
162 return false;
163 }
164
165 // For QString::fromLatin1("foo") returns "foo"
stringLiteralForCall(Stmt * call)166 static StringLiteral* stringLiteralForCall(Stmt *call)
167 {
168 if (!call)
169 return nullptr;
170
171 vector<StringLiteral*> literals;
172 clazy::getChilds(call, literals, 2);
173 return literals.empty() ? nullptr : literals[0];
174 }
175
VisitCtor(Stmt * stm)176 void QStringAllocations::VisitCtor(Stmt *stm)
177 {
178 CXXConstructExpr *ctorExpr = dyn_cast<CXXConstructExpr>(stm);
179 if (!Utils::containsStringLiteral(ctorExpr, /**allowEmpty=*/ true))
180 return;
181
182 CXXConstructorDecl *ctorDecl = ctorExpr->getConstructor();
183 if (!clazy::isOfClass(ctorDecl, "QString"))
184 return;
185
186 if (Utils::insideCTORCall(m_context->parentMap, stm, { "QRegExp", "QIcon" })) {
187 // https://blogs.kde.org/2015/11/05/qregexp-qstringliteral-crash-exit
188 return;
189 }
190
191 if (!isOptionSet("no-msvc-compat")) {
192 InitListExpr *initializerList = clazy::getFirstParentOfType<InitListExpr>(m_context->parentMap, ctorExpr);
193 if (initializerList != nullptr)
194 return; // Nothing to do here, MSVC doesn't like it
195
196 StringLiteral *lt = stringLiteralForCall(stm);
197 if (lt && lt->getNumConcatenated() > 1) {
198 return; // Nothing to do here, MSVC doesn't like it
199 }
200 }
201
202 bool isQLatin1String = false;
203 string paramType;
204 if (clazy::hasCharPtrArgument(ctorDecl, 1)) {
205 paramType = "const char*";
206 } else if (ctorDecl->param_size() == 1 && clazy::hasArgumentOfType(ctorDecl, "QLatin1String", lo())) {
207 paramType = "QLatin1String";
208 isQLatin1String = true;
209 } else {
210 return;
211 }
212
213 string msg = string("QString(") + paramType + string(") being called");
214
215 if (isQLatin1String) {
216 ConditionalOperator *ternary = nullptr;
217 Latin1Expr qlatin1expr = qlatin1CtorExpr(stm, ternary);
218 if (!qlatin1expr.isValid()) {
219 return;
220 }
221
222 auto qlatin1Ctor = qlatin1expr.qlatin1ctorexpr;
223
224
225 if (clazy::getLocStart(qlatin1Ctor).isMacroID()) {
226 auto macroName = Lexer::getImmediateMacroName(clazy::getLocStart(qlatin1Ctor), sm(), lo());
227 if (macroName == "Q_GLOBAL_STATIC_WITH_ARGS") // bug #391807
228 return;
229 }
230
231 vector<FixItHint> fixits;
232 if (qlatin1expr.enableFixit && isFixitEnabled(QLatin1StringAllocations)) {
233 if (!clazy::getLocStart(qlatin1Ctor).isMacroID()) {
234 if (!ternary) {
235 fixits = fixItReplaceWordWithWord(qlatin1Ctor, "QStringLiteral", "QLatin1String", QLatin1StringAllocations);
236 bool shouldRemoveQString = clazy::getLocStart(qlatin1Ctor).getRawEncoding() != clazy::getLocStart(stm).getRawEncoding() && dyn_cast_or_null<CXXBindTemporaryExpr>(clazy::parent(m_context->parentMap, ctorExpr));
237 if (shouldRemoveQString) {
238 // This is the case of QString(QLatin1String("foo")), which we just fixed to be QString(QStringLiteral("foo)), so now remove QString
239 auto removalFixits = clazy::fixItRemoveToken(&m_astContext, ctorExpr, true);
240 if (removalFixits.empty()) {
241 queueManualFixitWarning(clazy::getLocStart(ctorExpr), "Internal error: invalid start or end location", QLatin1StringAllocations);
242 } else {
243 clazy::append(removalFixits, fixits);
244 }
245 }
246 } else {
247 fixits = fixItReplaceWordWithWordInTernary(ternary);
248 }
249 } else {
250 queueManualFixitWarning(clazy::getLocStart(qlatin1Ctor), "Can't use QStringLiteral in macro", QLatin1StringAllocations);
251 }
252 }
253
254 emitWarning(clazy::getLocStart(stm), msg, fixits);
255 } else {
256 vector<FixItHint> fixits;
257 if (clazy::hasChildren(ctorExpr)) {
258 auto pointerDecay = dyn_cast<ImplicitCastExpr>(*(ctorExpr->child_begin()));
259 if (clazy::hasChildren(pointerDecay)) {
260 StringLiteral *lt = dyn_cast<StringLiteral>(*pointerDecay->child_begin());
261 if (lt && isFixitEnabled(CharPtrAllocations)) {
262 Stmt *grandParent = clazy::parent(m_context->parentMap, lt, 2);
263 Stmt *grandGrandParent = clazy::parent(m_context->parentMap, lt, 3);
264 Stmt *grandGrandGrandParent = clazy::parent(m_context->parentMap, lt, 4);
265 if (grandParent == ctorExpr && grandGrandParent && isa<CXXBindTemporaryExpr>(grandGrandParent) && grandGrandGrandParent && isa<CXXFunctionalCastExpr>(grandGrandGrandParent)) {
266 // This is the case of QString("foo"), replace QString
267
268 const bool literalIsEmpty = lt->getLength() == 0;
269 if (literalIsEmpty && clazy::getFirstParentOfType<MemberExpr>(m_context->parentMap, ctorExpr) == nullptr)
270 fixits = fixItReplaceWordWithWord(ctorExpr, "QLatin1String", "QString", CharPtrAllocations);
271 else if (!clazy::getLocStart(ctorExpr).isMacroID())
272 fixits = fixItReplaceWordWithWord(ctorExpr, "QStringLiteral", "QString", CharPtrAllocations);
273 else
274 queueManualFixitWarning(clazy::getLocStart(ctorExpr), "Can't use QStringLiteral in macro.", CharPtrAllocations);
275 } else {
276
277 auto parentMemberCallExpr = clazy::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap, lt, /*maxDepth=*/ 6); // 6 seems like a nice max from the ASTs I've seen
278
279 string replacement = "QStringLiteral";
280 if (parentMemberCallExpr) {
281 FunctionDecl *fDecl = parentMemberCallExpr->getDirectCallee();
282 if (fDecl) {
283 auto method = dyn_cast<CXXMethodDecl>(fDecl);
284 if (method && betterTakeQLatin1String(method, lt)) {
285 replacement = "QLatin1String";
286 }
287 }
288 }
289
290 fixits = fixItRawLiteral(lt, replacement);
291 }
292 }
293 }
294 }
295
296 emitWarning(clazy::getLocStart(stm), msg, fixits);
297 }
298 }
299
fixItReplaceWordWithWord(clang::Stmt * begin,const string & replacement,const string & replacee,int fixitType)300 vector<FixItHint> QStringAllocations::fixItReplaceWordWithWord(clang::Stmt *begin, const string &replacement, const string &replacee, int fixitType)
301 {
302 StringLiteral *lt = stringLiteralForCall(begin);
303 if (replacee == "QLatin1String") {
304 if (lt && !Utils::isAscii(lt)) {
305 emitWarning(clazy::getLocStart(lt), "Don't use QLatin1String with non-latin1 literals");
306 return {};
307 }
308 }
309
310 if (Utils::literalContainsEscapedBytes(lt, sm(), lo()))
311 return {};
312
313 vector<FixItHint> fixits;
314 FixItHint fixit = clazy::fixItReplaceWordWithWord(&m_astContext, begin, replacement, replacee);
315 if (fixit.isNull()) {
316 queueManualFixitWarning(clazy::getLocStart(begin), "", fixitType);
317 } else {
318 fixits.push_back(fixit);
319 }
320
321 return fixits;
322 }
323
fixItReplaceWordWithWordInTernary(clang::ConditionalOperator * ternary)324 vector<FixItHint> QStringAllocations::fixItReplaceWordWithWordInTernary(clang::ConditionalOperator *ternary)
325 {
326 vector<CXXConstructExpr*> constructExprs;
327 clazy::getChilds<CXXConstructExpr>(ternary, constructExprs, 1); // depth = 1, only the two immediate expressions
328
329 vector<FixItHint> fixits;
330 fixits.reserve(2);
331 if (constructExprs.size() != 2) {
332 llvm::errs() << "Weird ternary operator with " << constructExprs.size() << " at " << clazy::getLocStart(ternary).printToString(sm()) << "\n";
333 assert(false);
334 return fixits;
335 }
336
337 for (int i = 0; i < 2; ++i) {
338 SourceLocation rangeStart = clazy::getLocStart(constructExprs[i]);
339 SourceLocation rangeEnd = Lexer::getLocForEndOfToken(rangeStart, -1, sm(), lo());
340 fixits.push_back(FixItHint::CreateReplacement(SourceRange(rangeStart, rangeEnd), "QStringLiteral"));
341 }
342
343 return fixits;
344 }
345
346 // true for: QString::fromLatin1().arg()
347 // false for: QString::fromLatin1("")
348 // true for: QString s = QString::fromLatin1("foo")
349 // false for: s += QString::fromLatin1("foo"), etc.
isQStringLiteralCandidate(Stmt * s,ParentMap * map,const LangOptions & lo,const SourceManager & sm,int currentCall=0)350 static bool isQStringLiteralCandidate(Stmt *s, ParentMap *map, const LangOptions &lo,
351 const SourceManager &sm, int currentCall = 0)
352 {
353 if (!s)
354 return false;
355
356 MemberExpr *memberExpr = dyn_cast<MemberExpr>(s);
357 if (memberExpr)
358 return true;
359
360 auto constructExpr = dyn_cast<CXXConstructExpr>(s);
361 if (clazy::isOfClass(constructExpr, "QString"))
362 return true;
363
364 if (Utils::isAssignOperator(dyn_cast<CXXOperatorCallExpr>(s), "QString", "QLatin1String", lo))
365 return true;
366
367 if (Utils::isAssignOperator(dyn_cast<CXXOperatorCallExpr>(s), "QString", "QString", lo))
368 return true;
369
370 CallExpr *callExpr = dyn_cast<CallExpr>(s);
371 StringLiteral *literal = stringLiteralForCall(callExpr);
372 auto operatorCall = dyn_cast<CXXOperatorCallExpr>(s);
373 if (operatorCall && clazy::returnTypeName(operatorCall, lo) != "QTestData") {
374 // QTest::newRow will static_assert when using QLatin1String
375 // Q_STATIC_ASSERT_X(QMetaTypeId2<T>::Defined, "Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system");
376
377 string className = clazy::classNameFor(operatorCall);
378 if (className == "QString") {
379 return false;
380 } else if (className.empty() && clazy::hasArgumentOfType(operatorCall->getDirectCallee(), "QString", lo)) {
381 return false;
382 }
383 }
384
385 if (currentCall > 0 && callExpr) {
386 auto fDecl = callExpr->getDirectCallee();
387 if (fDecl && betterTakeQLatin1String(dyn_cast<CXXMethodDecl>(fDecl), literal))
388 return false;
389
390 return true;
391 }
392
393 if (currentCall == 0 || dyn_cast<ImplicitCastExpr>(s) || dyn_cast<CXXBindTemporaryExpr>(s) || dyn_cast<MaterializeTemporaryExpr>(s)) // skip this cruft
394 return isQStringLiteralCandidate(clazy::parent(map, s), map, lo, sm, currentCall + 1);
395
396 return false;
397 }
398
fixItReplaceFromLatin1OrFromUtf8(CallExpr * callExpr,FromFunction fromFunction)399 std::vector<FixItHint> QStringAllocations::fixItReplaceFromLatin1OrFromUtf8(CallExpr *callExpr, FromFunction fromFunction)
400 {
401 vector<FixItHint> fixits;
402
403 std::string replacement = isQStringLiteralCandidate(callExpr, m_context->parentMap, lo(), sm()) ? "QStringLiteral"
404 : "QLatin1String";
405
406 if (replacement == "QStringLiteral" && clazy::getLocStart(callExpr).isMacroID()) {
407 queueManualFixitWarning(clazy::getLocStart(callExpr), "Can't use QStringLiteral in macro!", FromLatin1_FromUtf8Allocations);
408 return {};
409 }
410
411 StringLiteral *literal = stringLiteralForCall(callExpr);
412 if (literal) {
413 if (Utils::literalContainsEscapedBytes(literal, sm(), lo()))
414 return {};
415 if (!Utils::isAscii(literal)) {
416 // QString::fromLatin1() to QLatin1String() is fine
417 // QString::fromUtf8() to QStringLiteral() is fine
418 // all other combinations are not
419 if (replacement == "QStringLiteral" && fromFunction == FromLatin1) {
420 return {};
421 } else if (replacement == "QLatin1String" && fromFunction == FromUtf8) {
422 replacement = "QStringLiteral";
423 }
424 }
425
426 auto classNameLoc = Lexer::getLocForEndOfToken(clazy::getLocStart(callExpr), 0, sm(), lo());
427 auto scopeOperatorLoc = Lexer::getLocForEndOfToken(classNameLoc, 0, sm(), lo());
428 auto methodNameLoc = Lexer::getLocForEndOfToken(scopeOperatorLoc, -1, sm(), lo());
429 SourceRange range(clazy::getLocStart(callExpr), methodNameLoc);
430 fixits.push_back(FixItHint::CreateReplacement(range, replacement));
431 } else {
432 queueManualFixitWarning(clazy::getLocStart(callExpr), "Internal error: literal is null", FromLatin1_FromUtf8Allocations);
433 }
434
435 return fixits;
436 }
437
fixItRawLiteral(clang::StringLiteral * lt,const string & replacement)438 std::vector<FixItHint> QStringAllocations::fixItRawLiteral(clang::StringLiteral *lt, const string &replacement)
439 {
440 vector<FixItHint> fixits;
441
442 SourceRange range = clazy::rangeForLiteral(&m_astContext, lt);
443 if (range.isInvalid()) {
444 if (lt) {
445 queueManualFixitWarning(clazy::getLocStart(lt), "Internal error: Can't calculate source location", CharPtrAllocations);
446 }
447 return {};
448 }
449
450 SourceLocation start = clazy::getLocStart(lt);
451 if (start.isMacroID()) {
452 queueManualFixitWarning(start, "Can't use QStringLiteral in macro", CharPtrAllocations);
453 } else {
454 if (Utils::literalContainsEscapedBytes(lt, sm(), lo()))
455 return {};
456
457 string revisedReplacement = lt->getLength() == 0 ? "QLatin1String" : replacement; // QLatin1String("") is better than QStringLiteral("")
458 if (revisedReplacement == "QStringLiteral" && clazy::getLocStart(lt).isMacroID()) {
459 queueManualFixitWarning(clazy::getLocStart(lt), "Can't use QStringLiteral in macro...", CharPtrAllocations);
460 return {};
461 }
462
463 clazy::insertParentMethodCall(revisedReplacement, range, /**by-ref*/ fixits);
464 }
465
466 return fixits;
467 }
468
VisitOperatorCall(Stmt * stm)469 void QStringAllocations::VisitOperatorCall(Stmt *stm)
470 {
471 CXXOperatorCallExpr *operatorCall = dyn_cast<CXXOperatorCallExpr>(stm);
472 if (!operatorCall)
473 return;
474
475 if (clazy::returnTypeName(operatorCall, lo()) == "QTestData") {
476 // QTest::newRow will static_assert when using QLatin1String
477 // Q_STATIC_ASSERT_X(QMetaTypeId2<T>::Defined, "Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system");
478 return;
479 }
480
481 std::vector<StringLiteral*> stringLiterals;
482 clazy::getChilds<StringLiteral>(operatorCall, stringLiterals);
483
484 // We're only after string literals, str.contains(some_method_returning_const_char_is_fine())
485 if (stringLiterals.empty())
486 return;
487
488 FunctionDecl *funcDecl = operatorCall->getDirectCallee();
489 if (!funcDecl)
490 return;
491
492 CXXMethodDecl *methodDecl = dyn_cast<CXXMethodDecl>(funcDecl);
493 if (!clazy::isOfClass(methodDecl, "QString"))
494 return;
495
496 if (!clazy::hasCharPtrArgument(methodDecl))
497 return;
498
499 vector<FixItHint> fixits;
500
501 vector<StringLiteral*> literals;
502 clazy::getChilds<StringLiteral>(stm, literals, 2);
503
504 if (!isOptionSet("no-msvc-compat") && !literals.empty()) {
505 if (literals[0]->getNumConcatenated() > 1) {
506 return; // Nothing to do here, MSVC doesn't like it
507 }
508 }
509
510 if (isFixitEnabled(CharPtrAllocations)) {
511 if (literals.empty()) {
512 queueManualFixitWarning(clazy::getLocStart(stm), "Couldn't find literal", CharPtrAllocations);
513 } else {
514 const string replacement = Utils::isAscii(literals[0]) ? "QLatin1String" : "QStringLiteral";
515 fixits = fixItRawLiteral(literals[0], replacement);
516 }
517 }
518
519 string msg = string("QString(const char*) being called");
520 emitWarning(clazy::getLocStart(stm), msg, fixits);
521 }
522
VisitFromLatin1OrUtf8(Stmt * stmt)523 void QStringAllocations::VisitFromLatin1OrUtf8(Stmt *stmt)
524 {
525 CallExpr *callExpr = dyn_cast<CallExpr>(stmt);
526 if (!callExpr)
527 return;
528
529 FunctionDecl *functionDecl = callExpr->getDirectCallee();
530 if (!clazy::functionIsOneOf(functionDecl, {"fromLatin1", "fromUtf8"}))
531 return;
532
533 CXXMethodDecl *methodDecl = dyn_cast<CXXMethodDecl>(functionDecl);
534 if (!clazy::isOfClass(methodDecl, "QString"))
535 return;
536
537 if (!Utils::callHasDefaultArguments(callExpr) || !clazy::hasCharPtrArgument(functionDecl, 2)) // QString::fromLatin1("foo", 1) is ok
538 return;
539
540 if (!containsStringLiteralNoCallExpr(callExpr))
541 return;
542
543 if (!isOptionSet("no-msvc-compat")) {
544 StringLiteral *lt = stringLiteralForCall(callExpr);
545 if (lt && lt->getNumConcatenated() > 1) {
546 return; // Nothing to do here, MSVC doesn't like it
547 }
548 }
549
550 vector<ConditionalOperator*> ternaries;
551 clazy::getChilds(callExpr, ternaries, 2);
552 if (!ternaries.empty()) {
553 auto ternary = ternaries[0];
554 if (Utils::ternaryOperatorIsOfStringLiteral(ternary)) {
555 emitWarning(clazy::getLocStart(stmt), string("QString::fromLatin1() being passed a literal"));
556 }
557
558 return;
559 }
560
561 std::vector<FixItHint> fixits;
562
563 if (isFixitEnabled(FromLatin1_FromUtf8Allocations)) {
564 const FromFunction fromFunction = clazy::name(functionDecl) == "fromLatin1" ? FromLatin1 : FromUtf8;
565 fixits = fixItReplaceFromLatin1OrFromUtf8(callExpr, fromFunction);
566 }
567
568 if (clazy::name(functionDecl) == "fromLatin1") {
569 emitWarning(clazy::getLocStart(stmt), string("QString::fromLatin1() being passed a literal"), fixits);
570 } else {
571 emitWarning(clazy::getLocStart(stmt), string("QString::fromUtf8() being passed a literal"), fixits);
572 }
573 }
574
VisitAssignOperatorQLatin1String(Stmt * stmt)575 void QStringAllocations::VisitAssignOperatorQLatin1String(Stmt *stmt)
576 {
577 CXXOperatorCallExpr *callExpr = dyn_cast<CXXOperatorCallExpr>(stmt);
578 if (!Utils::isAssignOperator(callExpr, "QString", "QLatin1String", lo()))
579 return;
580
581 if (!containsStringLiteralNoCallExpr(stmt))
582 return;
583
584 ConditionalOperator *ternary = nullptr;
585 Stmt *begin = qlatin1CtorExpr(stmt, ternary).qlatin1ctorexpr;
586
587 if (!begin)
588 return;
589
590 vector<FixItHint> fixits;
591
592 if (isFixitEnabled(QLatin1StringAllocations)) {
593 fixits = ternary == nullptr ? fixItReplaceWordWithWord(begin, "QStringLiteral", "QLatin1String", QLatin1StringAllocations)
594 : fixItReplaceWordWithWordInTernary(ternary);
595 }
596
597 emitWarning(clazy::getLocStart(stmt), string("QString::operator=(QLatin1String(\"literal\")"), fixits);
598 }
599