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 }