Commit f2aabc8c authored by Lucie Gerard's avatar Lucie Gerard Committed by Sergio Martins

Add signature fixes for qHash functions

Identify qHash functions not returning size_t and correct the return type
Also correct the function parameter to size_t when necessary
Only applicable for Qt6
parent cac973f8
......@@ -13,6 +13,7 @@ set(CLAZY_CHECKS_SRCS ${CLAZY_CHECKS_SRCS}
${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/qstring-varargs.cpp
${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/qt-keywords.cpp
${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/qt4-qstring-from-array.cpp
${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/qt6-qhash-signature.cpp
${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/qt6-qlatin1string-to-u.cpp
${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/qvariant-template-instantiation.cpp
${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/raw-environment-function.cpp
......
......@@ -228,6 +228,7 @@ clazy runs all checks from level1 by default.
- [qstring-varargs](docs/checks/README-qstring-varargs.md)
- [qt-keywords](docs/checks/README-qt-keywords.md) (fix-qt-keywords)
- [qt4-qstring-from-array](docs/checks/README-qt4-qstring-from-array.md) (fix-qt4-qstring-from-array)
- [qt6-qhash-signature](docs/checks/README-qt6-qhash-signature.md)
- [qt6-qlatin1string-to-u](docs/checks/README-qt6-qlatin1string-to-u.md) (fix-qt6-qlatin1string-to-u)
- [qvariant-template-instantiation](docs/checks/README-qvariant-template-instantiation.md)
- [raw-environment-function](docs/checks/README-raw-environment-function.md)
......
......@@ -77,6 +77,19 @@
],
"visits_stmts" : true
},
{
"name" : "qt6-qhash-signature",
"class_name" : "Qt6QHashSignature",
"level" : -1,
"categories" : ["qt6", "qhash"],
"fixits" : [
{
"name" : "qt6-qhash-signature"
}
],
"visits_decls" : true,
"visits_stmts" : true
},
{
"name" : "tr-non-literal",
"level" : -1,
......
# qt6-qhash-signature
Warns and corrects the signature for qHash.
uint qHash(MyType x, uint seed) is replaced with size_t qHash(MyType x, size_t seed)
Also corrects the signature for qHashBits, qHashRange and qHashRangeCommutative.
......@@ -13,6 +13,7 @@ SET(README_manuallevel_FILES
${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qstring-varargs.md
${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qt-keywords.md
${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qt4-qstring-from-array.md
${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qt6-qhash-signature.md
${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qt6-qlatin1string-to-u.md
${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qvariant-template-instantiation.md
${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-raw-environment-function.md
......
......@@ -41,6 +41,7 @@
#include "checks/manuallevel/qstring-varargs.h"
#include "checks/manuallevel/qt-keywords.h"
#include "checks/manuallevel/qt4-qstring-from-array.h"
#include "checks/manuallevel/qt6-qhash-signature.h"
#include "checks/manuallevel/qt6-qlatin1string-to-u.h"
#include "checks/manuallevel/qvariant-template-instantiation.h"
#include "checks/manuallevel/raw-environment-function.h"
......@@ -140,6 +141,8 @@ void CheckManager::registerChecks()
registerFixIt(1, "fix-qt-keywords", "qt-keywords");
registerCheck(check<Qt4QStringFromArray>("qt4-qstring-from-array", ManualCheckLevel, RegisteredCheck::Option_VisitsStmts));
registerFixIt(1, "fix-qt4-qstring-from-array", "qt4-qstring-from-array");
registerCheck(check<Qt6QHashSignature>("qt6-qhash-signature", ManualCheckLevel, RegisteredCheck::Option_VisitsStmts | RegisteredCheck::Option_VisitsDecls));
registerFixIt(1, "fix-qt6-qhash-signature", "qt6-qhash-signature");
registerCheck(check<Qt6QLatin1StringToU>("qt6-qlatin1string-to-u", ManualCheckLevel, RegisteredCheck::Option_VisitsStmts));
registerFixIt(1, "fix-qt6-qlatin1string-to-u", "qt6-qlatin1string-to-u");
registerCheck(check<QVariantTemplateInstantiation>("qvariant-template-instantiation", ManualCheckLevel, RegisteredCheck::Option_VisitsStmts));
......
/*
This file is part of the clazy static checker.
Copyright (C) 2020 The Qt Company Ltd.
Copyright (C) 2020 Lucie Gerard <lucie.gerard@qt.io>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "qt6-qhash-signature.h"
#include "ClazyContext.h"
#include "Utils.h"
#include "StringUtils.h"
#include "FixItUtils.h"
#include "HierarchyUtils.h"
#include "SourceCompatibilityHelpers.h"
#include "clazy_stl.h"
#include <clang/Lex/Lexer.h>
#include <clang/AST/Decl.h>
#include <clang/AST/DeclCXX.h>
#include <clang/AST/Expr.h>
#include <clang/AST/ExprCXX.h>
#include <clang/AST/Stmt.h>
#include <clang/AST/Type.h>
#include <clang/Basic/Diagnostic.h>
#include <clang/Basic/LLVM.h>
#include <clang/Basic/SourceLocation.h>
#include <llvm/ADT/ArrayRef.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/Casting.h>
using namespace clang;
using namespace std;
Qt6QHashSignature::Qt6QHashSignature(const std::string &name, ClazyContext *context)
: CheckBase(name, context, Option_CanIgnoreIncludes)
{
}
static bool isInterestingFunction(std::string name) {
if (name == "qHash" || name == "qHashBits" || name == "qHashRange" || name == "qHashRangeCommutative")
return true;
return false;
}
static int uintToSizetParam(clang::FunctionDecl *funcDecl)
{
std::string functionName = funcDecl->getNameAsString();
// the uint signature is on the second parameter for the qHash function
// it is on the third paramater for qHashBits, qHashRange and qHashCommutative
if (functionName == "qHash" && funcDecl->getNumParams() == 2)
return 1;
if ((functionName == "qHashBits" || functionName == "qHashRange" || functionName == "qHashRangeCommutative")
&& funcDecl->getNumParams() == 3)
return 2;
return -1;
}
static clang::ParmVarDecl* getInterestingParam(clang::FunctionDecl *funcDecl)
{
if (uintToSizetParam(funcDecl)>0)
return funcDecl->getParamDecl(uintToSizetParam(funcDecl));
return NULL;
}
static bool isWrongParamType(clang::FunctionDecl *funcDecl)
{
// Second or third parameter of the qHash functions should be size_t
auto param = getInterestingParam(funcDecl);
if (!param)
return false;
const string typeStr = param->getType().getAsString();
if (typeStr != "size_t") {
return true;
}
return false;
}
static bool isWrongReturnType(clang::FunctionDecl *funcDecl)
{
if (!funcDecl)
return false;
//Return type should be size_t
if (funcDecl->getReturnType().getAsString() != "size_t")
return true;
return false;
}
void Qt6QHashSignature::VisitStmt(clang::Stmt *stmt)
{
DeclRefExpr *declRefExpr = dyn_cast<DeclRefExpr>(stmt);
if (!declRefExpr)
return;
// checking if we're dealing with a qhash function
std::string name = declRefExpr->getNameInfo().getAsString();
if (!isInterestingFunction(name))
return;
VarDecl *varDecl = m_context->lastDecl ? dyn_cast<VarDecl>(m_context->lastDecl) : nullptr;
FieldDecl *fieldDecl = m_context->lastDecl ? dyn_cast<FieldDecl>(m_context->lastDecl) : nullptr;
FunctionDecl *funcDecl = m_context->lastDecl ? dyn_cast<FunctionDecl>(m_context->lastFunctionDecl) : nullptr;
if (!varDecl && !fieldDecl && !funcDecl)
return;
// need to check if this stmt is part of a return stmt, if it is, it's the lastFunctionDecl that is of interest
// loop over the parent until I find a return statement.
Stmt *parent = clazy::parent(m_context->parentMap, stmt);
bool isPartReturnStmt = false;
if (parent) {
while (parent) {
Stmt* ancester = clazy::parent(m_context->parentMap, parent);
if (!ancester)
break;
ReturnStmt *returnStmt = dyn_cast<ReturnStmt>(ancester);
if (returnStmt) {
isPartReturnStmt = true;
break;
}
parent = ancester;
}
}
// if the stmt is part of a return statement but there is no last functionDecl, it's a problem...
if (isPartReturnStmt && !funcDecl)
return;
std::string message;
std::string declType;
SourceRange fixitRange;
SourceLocation warningLocation;
if (funcDecl && isPartReturnStmt) {
// if the return correspond to a qHash function, we are not interested
// the qHash function is taken care of during the VisitDecl
if (isInterestingFunction(funcDecl->getNameAsString()))
return;
declType = funcDecl->getReturnType().getAsString();
fixitRange = funcDecl->getReturnTypeSourceRange();
warningLocation = clazy::getLocStart(funcDecl);
} else if (varDecl) {
declType = varDecl->getType().getAsString();
fixitRange = varDecl->getTypeSourceInfo()->getTypeLoc().getSourceRange();
warningLocation = clazy::getLocStart(varDecl);
} else if (fieldDecl) {
declType = fieldDecl->getType().getAsString();
fixitRange = fieldDecl->getTypeSourceInfo()->getTypeLoc().getSourceRange();
warningLocation = clazy::getLocStart(fieldDecl);
}
std::string qhashReturnType = declRefExpr->getDecl()->getAsFunction()->getReturnType().getAsString();
if (declType == "size_t" && qhashReturnType == "size_t")
return;
vector<FixItHint> fixits;
// if the type of the variable is correct, but the qHash is not returning the right type
// just emit warning...
if (declType == "size_t" && qhashReturnType != "size_t") {
message = name + " should return size_t";
emitWarning(clazy::getLocStart(declRefExpr), message, fixits);
return;
}
fixits.push_back(FixItHint::CreateReplacement(fixitRange, "size_t"));
if (qhashReturnType != "size_t") {
message = name + " should return size_t";
} else {
message = name + " returns size_t";
}
emitWarning(warningLocation, message, fixits);
return;
}
void Qt6QHashSignature::VisitDecl(clang::Decl *decl)
{
FunctionDecl *funcDecl = dyn_cast<FunctionDecl>(decl);
if (funcDecl) {
if (!isInterestingFunction(funcDecl->getNameAsString()))
return;
bool wrongReturnType = isWrongReturnType(funcDecl);
bool wrongParamType = isWrongParamType(funcDecl);
if (!wrongReturnType && !wrongParamType)
return;
vector<FixItHint> fixits;
string message;
message = funcDecl->getNameAsString() + " with uint signature";
fixits = fixitReplace(funcDecl, wrongReturnType, wrongParamType);
emitWarning(clazy::getLocStart(funcDecl), message, fixits);
}
return;
}
std::vector<FixItHint> Qt6QHashSignature::fixitReplace(clang::FunctionDecl *funcDecl, bool changeReturnType, bool changeParamType)
{
std::string replacement = "";
vector<FixItHint> fixits;
if (!funcDecl)
return fixits;
if (changeReturnType) {
replacement = "size_t";
fixits.push_back(FixItHint::CreateReplacement(funcDecl->getReturnTypeSourceRange(), replacement));
}
if (changeParamType) {
clang::SourceRange range = getInterestingParam(funcDecl)->getTypeSourceInfo()->getTypeLoc().getSourceRange();
replacement = "size_t";
fixits.push_back(FixItHint::CreateReplacement(range, replacement));
}
return fixits;
}
/*
This file is part of the clazy static checker.
Copyright (C) 2020 The Qt Company Ltd.
Copyright (C) 2020 Lucie Gerard <lucie.gerard@qt.io>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CLAZY_QT6_QHASH_SIGNATURE
#define CLAZY_QT6_QHASH_SIGNATURE
#include "checkbase.h"
#include <vector>
#include <string>
class ClazyContext;
namespace clang {
class Stmt;
class FixItHint;
class CXXConstructExpr;
class CXXOperatorCallExpr;
class Expr;
class CXXMemberCallExpr;
class CXXFunctionalCastExpr;
}
/**
* Replaces qhash signature uint with size_t.
*
* Run only in Qt 6 code.
*/
class Qt6QHashSignature
: public CheckBase
{
public:
explicit Qt6QHashSignature(const std::string &name, ClazyContext *context);
void VisitDecl(clang::Decl *decl) override;
void VisitStmt(clang::Stmt *stmt) override;
private:
std::vector<clang::FixItHint> fixitReplace(clang::FunctionDecl *funcDecl, bool changeReturnType, bool changeParamType);
};
#endif
{
"tests" : [
{
"filename" : "main.cpp",
"qt_major_version": 6,
"has_fixits" : true
}
]
}
#include <QtCore/QHash>
#include <QtCore/QString>
class Employee
{
public:
Employee() {}
Employee(const QString &name);
QString name() {return myName;}
uint test_var = qHash("blabla", 0);
private:
QString myName;
};
inline uint qHash(Employee *key, uint seed)
{
return qHash(key->name(), seed) ^ 4;
}
uint test_var_2 = qHash("blabla", 0);
uint anotherFunction()
{
int toto_NOT_qhash_related = 3;
return qHash("blabla", 0);
}
void test()
{
QString name = "Bob";
Employee *theemploye = new Employee(name);
uint foo = qHash(theemploye, 0);
size_t foo_g = qHash(theemploye, 0);
unsigned char p[2];
uint foo_bits = qHashBits(p, 0, 0);
static const int ints[] = {0, 1, 2, 3, 4, 5};
uint foo_range = qHashRange(ints, ints);
uint foo_rangec = qHashRangeCommutative(ints, ints);
}
qt6-qhash-signature/main.cpp:10:5: warning: qHash returns size_t [-Wclazy-qt6-qhash-signature]
qt6-qhash-signature/main.cpp:16:1: warning: qHash with uint signature [-Wclazy-qt6-qhash-signature]
qt6-qhash-signature/main.cpp:21:1: warning: qHash returns size_t [-Wclazy-qt6-qhash-signature]
qt6-qhash-signature/main.cpp:23:1: warning: qHash returns size_t [-Wclazy-qt6-qhash-signature]
qt6-qhash-signature/main.cpp:33:5: warning: qHash should return size_t [-Wclazy-qt6-qhash-signature]
qt6-qhash-signature/main.cpp:34:20: warning: qHash should return size_t [-Wclazy-qt6-qhash-signature]
qt6-qhash-signature/main.cpp:37:5: warning: qHashBits returns size_t [-Wclazy-qt6-qhash-signature]
qt6-qhash-signature/main.cpp:39:5: warning: qHashRange returns size_t [-Wclazy-qt6-qhash-signature]
qt6-qhash-signature/main.cpp:40:5: warning: qHashRangeCommutative returns size_t [-Wclazy-qt6-qhash-signature]
#include <QtCore/QHash>
#include <QtCore/QString>
class Employee
{
public:
Employee() {}
Employee(const QString &name);
QString name() {return myName;}
size_t test_var = qHash("blabla", 0);
private:
QString myName;
};
inline size_t qHash(Employee *key, size_t seed)
{
return qHash(key->name(), seed) ^ 4;
}
size_t test_var_2 = qHash("blabla", 0);
size_t anotherFunction()
{
int toto_NOT_qhash_related = 3;
return qHash("blabla", 0);
}
void test()
{
QString name = "Bob";
Employee *theemploye = new Employee(name);
size_t foo = qHash(theemploye, 0);
size_t foo_g = qHash(theemploye, 0);
unsigned char p[2];
size_t foo_bits = qHashBits(p, 0, 0);
static const int ints[] = {0, 1, 2, 3, 4, 5};
size_t foo_range = qHashRange(ints, ints);
size_t foo_rangec = qHashRangeCommutative(ints, ints);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment