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 }