File indexing completed on 2024-05-12 05:41:02
0001 /* 0002 SPDX-FileCopyrightText: 2020 The Qt Company Ltd. 0003 SPDX-FileCopyrightText: 2020 Lucie Gerard <lucie.gerard@qt.io> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "qt6-qhash-signature.h" 0009 #include "ClazyContext.h" 0010 #include "FixItUtils.h" 0011 #include "HierarchyUtils.h" 0012 #include "Utils.h" 0013 0014 #include <clang/AST/Decl.h> 0015 #include <clang/AST/DeclCXX.h> 0016 #include <clang/AST/Expr.h> 0017 #include <clang/AST/ExprCXX.h> 0018 #include <clang/AST/Stmt.h> 0019 #include <clang/AST/Type.h> 0020 #include <clang/Basic/Diagnostic.h> 0021 #include <clang/Basic/LLVM.h> 0022 #include <clang/Basic/SourceLocation.h> 0023 #include <clang/Lex/Lexer.h> 0024 #include <llvm/ADT/ArrayRef.h> 0025 #include <llvm/ADT/StringRef.h> 0026 #include <llvm/Support/Casting.h> 0027 0028 using namespace clang; 0029 0030 Qt6QHashSignature::Qt6QHashSignature(const std::string &name, ClazyContext *context) 0031 : CheckBase(name, context, Option_CanIgnoreIncludes) 0032 { 0033 } 0034 0035 static bool isInterestingFunction(const std::string &name) 0036 { 0037 return name == "qHash" || name == "qHashBits" || name == "qHashRange" || name == "qHashRangeCommutative"; 0038 } 0039 0040 static int uintToSizetParam(clang::FunctionDecl *funcDecl) 0041 { 0042 std::string functionName = funcDecl->getNameAsString(); 0043 // the uint signature is on the second parameter for the qHash function 0044 // it is on the third paramater for qHashBits, qHashRange and qHashCommutative 0045 if (functionName == "qHash" && funcDecl->getNumParams() == 2) { 0046 return 1; 0047 } 0048 if ((functionName == "qHashBits" || functionName == "qHashRange" || functionName == "qHashRangeCommutative") && funcDecl->getNumParams() == 3) { 0049 return 2; 0050 } 0051 0052 return -1; 0053 } 0054 0055 static clang::ParmVarDecl *getInterestingParam(clang::FunctionDecl *funcDecl) 0056 { 0057 if (uintToSizetParam(funcDecl) > 0) { 0058 return funcDecl->getParamDecl(uintToSizetParam(funcDecl)); 0059 } 0060 return nullptr; 0061 } 0062 0063 static bool isWrongParamType(clang::FunctionDecl *funcDecl) 0064 { 0065 // Second or third parameter of the qHash functions should be size_t 0066 auto *param = getInterestingParam(funcDecl); 0067 if (!param) { 0068 return false; 0069 } 0070 const std::string typeStr = param->getType().getAsString(); 0071 return typeStr != "size_t"; 0072 } 0073 0074 static bool isWrongReturnType(clang::FunctionDecl *funcDecl) 0075 { 0076 if (!funcDecl) { 0077 return false; 0078 } 0079 0080 // Return type should be size_t 0081 if (funcDecl->getReturnType().getAsString() != "size_t") { 0082 return true; 0083 } 0084 return false; 0085 } 0086 0087 void Qt6QHashSignature::VisitStmt(clang::Stmt *stmt) 0088 { 0089 auto *declRefExpr = dyn_cast<DeclRefExpr>(stmt); 0090 if (!declRefExpr) { 0091 return; 0092 } 0093 0094 // checking if we're dealing with a qhash function 0095 std::string name = declRefExpr->getNameInfo().getAsString(); 0096 if (!isInterestingFunction(name)) { 0097 return; 0098 } 0099 0100 VarDecl *varDecl = m_context->lastDecl ? dyn_cast<VarDecl>(m_context->lastDecl) : nullptr; 0101 FieldDecl *fieldDecl = m_context->lastDecl ? dyn_cast<FieldDecl>(m_context->lastDecl) : nullptr; 0102 FunctionDecl *funcDecl = m_context->lastDecl ? dyn_cast<FunctionDecl>(m_context->lastFunctionDecl) : nullptr; 0103 0104 if (!varDecl && !fieldDecl && !funcDecl) { 0105 return; 0106 } 0107 0108 // need to check if this stmt is part of a return stmt, if it is, it's the lastFunctionDecl that is of interest 0109 // loop over the parent until I find a return statement. 0110 Stmt *parent = clazy::parent(m_context->parentMap, stmt); 0111 bool isPartReturnStmt = false; 0112 if (parent) { 0113 while (parent) { 0114 Stmt *ancester = clazy::parent(m_context->parentMap, parent); 0115 if (!ancester) { 0116 break; 0117 } 0118 auto *returnStmt = dyn_cast<ReturnStmt>(ancester); 0119 if (returnStmt) { 0120 isPartReturnStmt = true; 0121 break; 0122 } 0123 parent = ancester; 0124 } 0125 } 0126 0127 // if the stmt is part of a return statement but there is no last functionDecl, it's a problem... 0128 if (isPartReturnStmt && !funcDecl) { 0129 return; 0130 } 0131 0132 std::string message; 0133 std::string declType; 0134 SourceRange fixitRange; 0135 SourceLocation warningLocation; 0136 0137 if (funcDecl && isPartReturnStmt) { 0138 // if the return correspond to a qHash function, we are not interested 0139 // the qHash function is taken care of during the VisitDecl 0140 if (isInterestingFunction(funcDecl->getNameAsString())) { 0141 return; 0142 } 0143 declType = funcDecl->getReturnType().getAsString(); 0144 fixitRange = funcDecl->getReturnTypeSourceRange(); 0145 warningLocation = funcDecl->getBeginLoc(); 0146 } else if (varDecl) { 0147 declType = varDecl->getType().getAsString(); 0148 fixitRange = varDecl->getTypeSourceInfo()->getTypeLoc().getSourceRange(); 0149 warningLocation = varDecl->getBeginLoc(); 0150 } else if (fieldDecl) { 0151 declType = fieldDecl->getType().getAsString(); 0152 fixitRange = fieldDecl->getTypeSourceInfo()->getTypeLoc().getSourceRange(); 0153 warningLocation = fieldDecl->getBeginLoc(); 0154 } 0155 0156 std::string qhashReturnType = declRefExpr->getDecl()->getAsFunction()->getReturnType().getAsString(); 0157 if (declType == "size_t" && qhashReturnType == "size_t") { 0158 return; 0159 } 0160 0161 std::vector<FixItHint> fixits; 0162 0163 // if the type of the variable is correct, but the qHash is not returning the right type 0164 // just emit warning... 0165 if (declType == "size_t" && qhashReturnType != "size_t") { 0166 message = name + " should return size_t"; 0167 emitWarning(declRefExpr->getBeginLoc(), message, fixits); 0168 return; 0169 } 0170 0171 fixits.push_back(FixItHint::CreateReplacement(fixitRange, "size_t")); 0172 0173 if (qhashReturnType != "size_t") { 0174 message = name + " should return size_t"; 0175 } else { 0176 message = name + " returns size_t"; 0177 } 0178 emitWarning(warningLocation, message, fixits); 0179 0180 return; 0181 } 0182 0183 void Qt6QHashSignature::VisitDecl(clang::Decl *decl) 0184 { 0185 auto *funcDecl = dyn_cast<FunctionDecl>(decl); 0186 if (funcDecl) { 0187 if (!isInterestingFunction(funcDecl->getNameAsString())) { 0188 return; 0189 } 0190 0191 bool wrongReturnType = isWrongReturnType(funcDecl); 0192 bool wrongParamType = isWrongParamType(funcDecl); 0193 if (!wrongReturnType && !wrongParamType) { 0194 return; 0195 } 0196 std::vector<FixItHint> fixits; 0197 std::string message; 0198 message = funcDecl->getNameAsString() + " with uint signature"; 0199 fixits = fixitReplace(funcDecl, wrongReturnType, wrongParamType); 0200 emitWarning(funcDecl->getBeginLoc(), message, fixits); 0201 } 0202 0203 return; 0204 } 0205 0206 std::vector<FixItHint> Qt6QHashSignature::fixitReplace(clang::FunctionDecl *funcDecl, bool changeReturnType, bool changeParamType) 0207 { 0208 std::string replacement; 0209 std::vector<FixItHint> fixits; 0210 if (!funcDecl) { 0211 return fixits; 0212 } 0213 0214 if (changeReturnType) { 0215 replacement = "size_t"; 0216 fixits.push_back(FixItHint::CreateReplacement(funcDecl->getReturnTypeSourceRange(), replacement)); 0217 } 0218 if (changeParamType) { 0219 clang::SourceRange range = getInterestingParam(funcDecl)->getTypeSourceInfo()->getTypeLoc().getSourceRange(); 0220 replacement = "size_t"; 0221 fixits.push_back(FixItHint::CreateReplacement(range, replacement)); 0222 } 0223 return fixits; 0224 }