File indexing completed on 2024-05-12 05:41:02

0001 /*
0002     SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com
0003     SPDX-FileContributor: SĂ©rgio Martins <sergio.martins@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "qt4-qstring-from-array.h"
0009 #include "ClazyContext.h"
0010 #include "FixItUtils.h"
0011 #include "HierarchyUtils.h"
0012 #include "StringUtils.h"
0013 #include "Utils.h"
0014 #include "clazy_stl.h"
0015 
0016 #include <clang/AST/Decl.h>
0017 #include <clang/AST/DeclCXX.h>
0018 #include <clang/AST/Expr.h>
0019 #include <clang/AST/ExprCXX.h>
0020 #include <clang/AST/Stmt.h>
0021 #include <clang/AST/Type.h>
0022 #include <clang/Basic/Diagnostic.h>
0023 #include <clang/Basic/LLVM.h>
0024 #include <clang/Basic/SourceLocation.h>
0025 #include <clang/Lex/Lexer.h>
0026 #include <llvm/ADT/ArrayRef.h>
0027 #include <llvm/ADT/StringRef.h>
0028 #include <llvm/Support/Casting.h>
0029 
0030 using namespace clang;
0031 
0032 Qt4QStringFromArray::Qt4QStringFromArray(const std::string &name, ClazyContext *context)
0033     : CheckBase(name, context, Option_CanIgnoreIncludes)
0034 {
0035 }
0036 
0037 static bool isInterestingParam(ParmVarDecl *param, bool &is_char_array, bool &is_byte_array)
0038 {
0039     is_char_array = false;
0040     is_byte_array = false;
0041     const std::string typeStr = param->getType().getAsString();
0042     if (typeStr == "const class QByteArray &") {
0043         is_byte_array = true;
0044     } else if (typeStr == "const char *") { // We only want bytearray and const char*
0045         is_char_array = true;
0046     }
0047 
0048     return is_char_array || is_byte_array;
0049 }
0050 
0051 static bool isInterestingCtorCall(CXXConstructorDecl *ctor, bool &is_char_array, bool &is_byte_array)
0052 {
0053     is_char_array = false;
0054     is_byte_array = false;
0055     if (!ctor || !clazy::isOfClass(ctor, "QString")) {
0056         return false;
0057     }
0058 
0059     for (auto *param : Utils::functionParameters(ctor)) {
0060         if (isInterestingParam(param, is_char_array, is_byte_array)) {
0061             break;
0062         }
0063 
0064         return false;
0065     }
0066 
0067     return is_char_array || is_byte_array;
0068 }
0069 
0070 static bool isInterestingMethod(const std::string &methodName)
0071 {
0072     static const std::vector<std::string> methods =
0073         {"append", "prepend", "operator=", "operator==", "operator!=", "operator<", "operator<=", "operator>", "operator>=", "operator+="};
0074     return clazy::contains(methods, methodName);
0075 }
0076 
0077 static bool isInterestingMethodCall(CXXMethodDecl *method, std::string &methodName, bool &is_char_array, bool &is_byte_array)
0078 {
0079     is_char_array = false;
0080     is_byte_array = false;
0081     if (!method) {
0082         return false;
0083     }
0084 
0085     if (clazy::name(method->getParent()) != "QString" || method->getNumParams() != 1) {
0086         return false;
0087     }
0088 
0089     methodName = method->getNameAsString();
0090     if (!isInterestingMethod(methodName)) {
0091         return false;
0092     }
0093 
0094     if (!isInterestingParam(method->getParamDecl(0), is_char_array, is_byte_array)) {
0095         return false;
0096     }
0097 
0098     return true;
0099 }
0100 
0101 static bool isInterestingOperatorCall(CXXOperatorCallExpr *op, std::string &operatorName, bool &is_char_array, bool &is_byte_array)
0102 {
0103     is_char_array = false;
0104     is_byte_array = false;
0105     FunctionDecl *func = op->getDirectCallee();
0106     if (!func) {
0107         return false;
0108     }
0109 
0110     return isInterestingMethodCall(dyn_cast<CXXMethodDecl>(func), operatorName, is_char_array, is_byte_array);
0111 }
0112 
0113 void Qt4QStringFromArray::VisitStmt(clang::Stmt *stm)
0114 {
0115     auto *ctorExpr = dyn_cast<CXXConstructExpr>(stm);
0116     auto *operatorCall = dyn_cast<CXXOperatorCallExpr>(stm);
0117     auto *memberCall = dyn_cast<CXXMemberCallExpr>(stm);
0118     if (!ctorExpr && !operatorCall && !memberCall) {
0119         return;
0120     }
0121 
0122     std::vector<FixItHint> fixits;
0123     bool is_char_array = false;
0124     bool is_byte_array = false;
0125     std::string methodName;
0126     std::string message;
0127 
0128     if (ctorExpr) {
0129         CXXConstructorDecl *ctorDecl = ctorExpr->getConstructor();
0130 
0131         if (!isInterestingCtorCall(ctorDecl, is_char_array, is_byte_array)) {
0132             return;
0133         }
0134 
0135         fixits = fixCtorCall(ctorExpr);
0136         if (is_char_array) {
0137             message = "QString(const char *) ctor being called";
0138         } else {
0139             message = "QString(QByteArray) ctor being called";
0140         }
0141     } else if (operatorCall) {
0142         if (!isInterestingOperatorCall(operatorCall, /*by-ref*/ methodName, is_char_array, is_byte_array)) {
0143             return;
0144         }
0145 
0146         fixits = fixOperatorCall(operatorCall);
0147     } else if (memberCall) {
0148         if (!isInterestingMethodCall(memberCall->getMethodDecl(), /*by-ref*/ methodName, is_char_array, is_byte_array)) {
0149             return;
0150         }
0151 
0152         fixits = fixMethodCallCall(memberCall);
0153     } else {
0154         return;
0155     }
0156 
0157     if (operatorCall || memberCall) {
0158         if (is_char_array) {
0159             message = "QString::" + methodName + "(const char *) being called";
0160         } else {
0161             message = "QString::" + methodName + "(QByteArray) being called";
0162         }
0163     }
0164 
0165     emitWarning(stm->getBeginLoc(), message, fixits);
0166 }
0167 
0168 std::vector<FixItHint> Qt4QStringFromArray::fixCtorCall(CXXConstructExpr *ctorExpr)
0169 {
0170     Stmt *parent = clazy::parent(m_context->parentMap, ctorExpr); // CXXBindTemporaryExpr
0171     Stmt *grandParent = clazy::parent(m_context->parentMap, parent); // CXXFunctionalCastExpr
0172 
0173     if (parent && grandParent && isa<CXXBindTemporaryExpr>(parent) && isa<CXXFunctionalCastExpr>(grandParent)) {
0174         return fixitReplaceWithFromLatin1(ctorExpr);
0175     }
0176     return fixitInsertFromLatin1(ctorExpr);
0177 }
0178 
0179 std::vector<FixItHint> Qt4QStringFromArray::fixOperatorCall(CXXOperatorCallExpr *op)
0180 {
0181     std::vector<FixItHint> fixits;
0182     if (op->getNumArgs() == 2) {
0183         Expr *e = op->getArg(1);
0184         SourceLocation start = e->getBeginLoc();
0185         SourceLocation end = Lexer::getLocForEndOfToken(clazy::biggestSourceLocationInStmt(sm(), e), 0, sm(), lo());
0186 
0187         SourceRange range = {start, end};
0188         if (range.isInvalid()) {
0189             emitWarning(op->getBeginLoc(), "internal error");
0190             return {};
0191         }
0192 
0193         clazy::insertParentMethodCall("QString::fromLatin1", {start, end}, /*by-ref*/ fixits);
0194 
0195     } else {
0196         emitWarning(op->getBeginLoc(), "internal error");
0197     }
0198 
0199     return fixits;
0200 }
0201 
0202 std::vector<FixItHint> Qt4QStringFromArray::fixMethodCallCall(clang::CXXMemberCallExpr *memberExpr)
0203 {
0204     std::vector<FixItHint> fixits;
0205 
0206     if (memberExpr->getNumArgs() == 1) {
0207         Expr *e = *(memberExpr->arg_begin());
0208         SourceLocation start = e->getBeginLoc();
0209         SourceLocation end = Lexer::getLocForEndOfToken(clazy::biggestSourceLocationInStmt(sm(), e), 0, sm(), lo());
0210 
0211         SourceRange range = {start, end};
0212         if (range.isInvalid()) {
0213             emitWarning(memberExpr->getBeginLoc(), "internal error");
0214             return {};
0215         }
0216 
0217         clazy::insertParentMethodCall("QString::fromLatin1", {start, end}, /*by-ref*/ fixits);
0218     } else {
0219         emitWarning(memberExpr->getBeginLoc(), "internal error");
0220     }
0221 
0222     return fixits;
0223 }
0224 
0225 std::vector<FixItHint> Qt4QStringFromArray::fixitReplaceWithFromLatin1(CXXConstructExpr *ctorExpr)
0226 {
0227     const std::string replacement = "QString::fromLatin1";
0228     const std::string replacee = "QString";
0229     std::vector<FixItHint> fixits;
0230 
0231     SourceLocation rangeStart = ctorExpr->getBeginLoc();
0232     SourceLocation rangeEnd = Lexer::getLocForEndOfToken(rangeStart, -1, sm(), lo());
0233 
0234     if (rangeEnd.isInvalid()) {
0235         // Fallback. Have seen a case in the wild where the above would fail, it's very rare
0236         rangeEnd = rangeStart.getLocWithOffset(replacee.size() - 2);
0237         if (rangeEnd.isInvalid()) {
0238             clazy::printLocation(sm(), rangeStart);
0239             clazy::printLocation(sm(), rangeEnd);
0240             clazy::printLocation(sm(), Lexer::getLocForEndOfToken(rangeStart, 0, sm(), lo()));
0241             queueManualFixitWarning(ctorExpr->getBeginLoc());
0242             return {};
0243         }
0244     }
0245 
0246     fixits.push_back(FixItHint::CreateReplacement(SourceRange(rangeStart, rangeEnd), replacement));
0247     return fixits;
0248 }
0249 
0250 std::vector<FixItHint> Qt4QStringFromArray::fixitInsertFromLatin1(CXXConstructExpr *ctorExpr)
0251 {
0252     std::vector<FixItHint> fixits;
0253     SourceRange range;
0254 
0255     Expr *arg = *(ctorExpr->arg_begin());
0256     range.setBegin(arg->getBeginLoc());
0257     range.setEnd(Lexer::getLocForEndOfToken(clazy::biggestSourceLocationInStmt(sm(), ctorExpr), 0, sm(), lo()));
0258     if (range.isInvalid()) {
0259         emitWarning(ctorExpr->getBeginLoc(), "Internal error");
0260         return {};
0261     }
0262 
0263     clazy::insertParentMethodCall("QString::fromLatin1", range, fixits);
0264 
0265     return fixits;
0266 }