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