void QStringAllocations::VisitFromLatin1OrUtf8(Stmt *stmt) { CallExpr *callExpr = dyn_cast<CallExpr>(stmt); if (!callExpr) return; FunctionDecl *functionDecl = callExpr->getDirectCallee(); if (!StringUtils::functionIsOneOf(functionDecl, {"fromLatin1", "fromUtf8"})) return; CXXMethodDecl *methodDecl = dyn_cast<CXXMethodDecl>(functionDecl); if (!StringUtils::isOfClass(methodDecl, "QString")) return; if (!Utils::callHasDefaultArguments(callExpr) || !hasCharPtrArgument(functionDecl, 2)) // QString::fromLatin1("foo", 1) is ok return; if (!containsStringLiteralNoCallExpr(callExpr)) return; if (!isOptionSet("no-msvc-compat")) { StringLiteral *lt = stringLiteralForCall(callExpr); if (lt && lt->getNumConcatenated() > 1) { return; // Nothing to do here, MSVC doesn't like it } } vector<ConditionalOperator*> ternaries; HierarchyUtils::getChilds(callExpr, ternaries, 2); if (!ternaries.empty()) { auto ternary = ternaries[0]; if (Utils::ternaryOperatorIsOfStringLiteral(ternary)) { emitWarning(stmt->getLocStart(), string("QString::fromLatin1() being passed a literal")); } return; } std::vector<FixItHint> fixits; if (isFixitEnabled(FromLatin1_FromUtf8Allocations)) { const FromFunction fromFunction = functionDecl->getNameAsString() == "fromLatin1" ? FromLatin1 : FromUtf8; fixits = fixItReplaceFromLatin1OrFromUtf8(callExpr, fromFunction); } if (functionDecl->getNameAsString() == "fromLatin1") { emitWarning(stmt->getLocStart(), string("QString::fromLatin1() being passed a literal"), fixits); } else { emitWarning(stmt->getLocStart(), string("QString::fromUtf8() being passed a literal"), fixits); } }
void QStringAllocations::VisitCtor(Stmt *stm) { CXXConstructExpr *ctorExpr = dyn_cast<CXXConstructExpr>(stm); if (!Utils::containsStringLiteral(ctorExpr, /**allowEmpty=*/ true)) return; CXXConstructorDecl *ctorDecl = ctorExpr->getConstructor(); if (!StringUtils::isOfClass(ctorDecl, "QString")) return; static const vector<string> blacklistedParentCtors = { "QRegExp", "QIcon" }; if (Utils::insideCTORCall(m_parentMap, stm, blacklistedParentCtors)) { // https://blogs.kde.org/2015/11/05/qregexp-qstringliteral-crash-exit return; } if (!isOptionSet("no-msvc-compat")) { InitListExpr *initializerList = HierarchyUtils::getFirstParentOfType<InitListExpr>(m_parentMap, ctorExpr); if (initializerList != nullptr) return; // Nothing to do here, MSVC doesn't like it StringLiteral *lt = stringLiteralForCall(stm); if (lt && lt->getNumConcatenated() > 1) { return; // Nothing to do here, MSVC doesn't like it } } bool isQLatin1String = false; string paramType; if (hasCharPtrArgument(ctorDecl, 1)) { paramType = "const char*"; } else if (ctorDecl->param_size() == 1 && StringUtils::hasArgumentOfType(ctorDecl, "QLatin1String", lo())) { paramType = "QLatin1String"; isQLatin1String = true; } else { return; } string msg = string("QString(") + paramType + string(") being called"); if (isQLatin1String) { ConditionalOperator *ternary = nullptr; Latin1Expr qlatin1expr = qlatin1CtorExpr(stm, ternary); if (!qlatin1expr.isValid()) { return; } auto qlatin1Ctor = qlatin1expr.qlatin1ctorexpr; vector<FixItHint> fixits; if (qlatin1expr.enableFixit && isFixitEnabled(QLatin1StringAllocations)) { if (!qlatin1Ctor->getLocStart().isMacroID()) { if (!ternary) { fixits = fixItReplaceWordWithWord(qlatin1Ctor, "QStringLiteral", "QLatin1String", QLatin1StringAllocations); bool shouldRemoveQString = qlatin1Ctor->getLocStart().getRawEncoding() != stm->getLocStart().getRawEncoding() && dyn_cast_or_null<CXXBindTemporaryExpr>(HierarchyUtils::parent(m_parentMap, ctorExpr)); if (shouldRemoveQString) { // This is the case of QString(QLatin1String("foo")), which we just fixed to be QString(QStringLiteral("foo)), so now remove QString auto removalFixits = FixItUtils::fixItRemoveToken(ci(), ctorExpr, true); if (removalFixits.empty()) { queueManualFixitWarning(ctorExpr->getLocStart(), QLatin1StringAllocations, "Internal error: invalid start or end location"); } else { clazy_std::append(removalFixits, fixits); } } } else { fixits = fixItReplaceWordWithWordInTernary(ternary); } } else { queueManualFixitWarning(qlatin1Ctor->getLocStart(), QLatin1StringAllocations, "Can't use QStringLiteral in macro"); } } emitWarning(stm->getLocStart(), msg, fixits); } else { vector<FixItHint> fixits; if (clazy_std::hasChildren(ctorExpr)) { auto pointerDecay = dyn_cast<ImplicitCastExpr>(*(ctorExpr->child_begin())); if (clazy_std::hasChildren(pointerDecay)) { StringLiteral *lt = dyn_cast<StringLiteral>(*pointerDecay->child_begin()); if (lt && isFixitEnabled(CharPtrAllocations)) { Stmt *grandParent = HierarchyUtils::parent(m_parentMap, lt, 2); Stmt *grandGrandParent = HierarchyUtils::parent(m_parentMap, lt, 3); Stmt *grandGrandGrandParent = HierarchyUtils::parent(m_parentMap, lt, 4); if (grandParent == ctorExpr && grandGrandParent && isa<CXXBindTemporaryExpr>(grandGrandParent) && grandGrandGrandParent && isa<CXXFunctionalCastExpr>(grandGrandGrandParent)) { // This is the case of QString("foo"), replace QString const bool literalIsEmpty = lt->getLength() == 0; if (literalIsEmpty && HierarchyUtils::getFirstParentOfType<MemberExpr>(m_parentMap, ctorExpr) == nullptr) fixits = fixItReplaceWordWithWord(ctorExpr, "QLatin1String", "QString", CharPtrAllocations); else if (!ctorExpr->getLocStart().isMacroID()) fixits = fixItReplaceWordWithWord(ctorExpr, "QStringLiteral", "QString", CharPtrAllocations); else queueManualFixitWarning(ctorExpr->getLocStart(), CharPtrAllocations, "Can't use QStringLiteral in macro."); } else { auto parentMemberCallExpr = HierarchyUtils::getFirstParentOfType<CXXMemberCallExpr>(m_parentMap, lt, /*maxDepth=*/6); // 6 seems like a nice max from the ASTs I've seen string replacement = "QStringLiteral"; if (parentMemberCallExpr) { FunctionDecl *fDecl = parentMemberCallExpr->getDirectCallee(); if (fDecl) { CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(fDecl); if (method && betterTakeQLatin1String(method, lt)) { replacement = "QLatin1String"; } } } fixits = fixItRawLiteral(lt, replacement); } } } } emitWarning(stm->getLocStart(), msg, fixits); } }