QDebug: cast the QFlags value to the right-sized unsigned type (1/2)

This change fixes three problems in the non-Q_ENUM overload. First, the
printing of the sign bit for a signed flag. This is correct, but
unexpected:
  QFlags(0x1|0x2|-0x80000000)
By using unsigned types, we'll print instead:
  QFlags(0x1|0x2|0x80000000)

Second, shifting into the sign bit is UB, so we remove the problem by
not having a sign bit at all.

Third, this provides an out-of-line non-template overload of the
implementation for unsigned QFlags, thereby avoiding an unnecessary
instantiation of the template function qt_QMetaEnum_flagDebugOperator()
in user code.

Pick-to: 6.8
Change-Id: I8a96935cf6c742259c9dfffd17e992caa315e1d3
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Thiago Macieira 2024-08-07 15:28:51 -07:00
parent 8fa8c574c0
commit 91a27c1a51
4 changed files with 19 additions and 8 deletions

View File

@ -984,6 +984,13 @@ QDataStream &QDataStream::operator<<(bool i)
return (*this << qint8(i)); return (*this << qint8(i));
} }
#include "qdebug.h"
Q_CORE_EXPORT void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, int value)
{
qt_QMetaEnum_flagDebugOperator(debug, sizeofT, uint(value));
}
#include "qdir.h" // inlined API #include "qdir.h" // inlined API
bool QDir::operator==(const QDir &dir) const bool QDir::operator==(const QDir &dir) const

View File

@ -1323,13 +1323,13 @@ QDebugStateSaver::~QDebugStateSaver()
\internal \internal
Specialization of the primary template in qdebug.h to out-of-line Specialization of the primary template in qdebug.h to out-of-line
the common case of QFlags<T>::Int being int. the common case of QFlags<T>::Int being 32-bit.
Just call the generic version so the two don't get out of sync. Just call the generic version so the two don't get out of sync.
*/ */
void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, int value) void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, uint value)
{ {
qt_QMetaEnum_flagDebugOperator<int>(debug, sizeofT, value); qt_QMetaEnum_flagDebugOperator<uint>(debug, sizeofT, value);
} }
#ifndef QT_NO_QOBJECT #ifndef QT_NO_QOBJECT

View File

@ -523,11 +523,13 @@ inline QDebug operator<<(QDebug debug, const QTaggedPointer<T, Tag> &ptr)
return debug; return debug;
} }
Q_CORE_EXPORT void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, int value); Q_CORE_EXPORT void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, uint value);
template <typename Int> template <typename Int>
void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, Int value) void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, Int value)
{ {
static_assert(std::is_unsigned_v<Int>,
"Cast value to an unsigned type before calling this function");
const QDebugStateSaver saver(debug); const QDebugStateSaver saver(debug);
debug.resetFormat(); debug.resetFormat();
debug.nospace() << "QFlags(" << Qt::hex << Qt::showbase; debug.nospace() << "QFlags(" << Qt::hex << Qt::showbase;
@ -591,7 +593,8 @@ template <class T>
inline QDebug qt_QMetaEnum_flagDebugOperator_helper(QDebug debug, const QFlags<T> &flags) inline QDebug qt_QMetaEnum_flagDebugOperator_helper(QDebug debug, const QFlags<T> &flags)
#endif #endif
{ {
qt_QMetaEnum_flagDebugOperator(debug, sizeof(T), typename QFlags<T>::Int(flags)); using UInt = typename QIntegerForSizeof<T>::Unsigned;
qt_QMetaEnum_flagDebugOperator(debug, sizeof(T), UInt(flags.toInt()));
return debug; return debug;
} }

View File

@ -1085,7 +1085,8 @@ void tst_QDebug::qDebugQByteArrayView() const
enum TestEnum { enum TestEnum {
Flag1 = 0x1, Flag1 = 0x1,
Flag2 = 0x10 Flag2 = 0x10,
SignFlag = INT_MIN,
}; };
Q_DECLARE_FLAGS(TestFlags, TestEnum) Q_DECLARE_FLAGS(TestFlags, TestEnum)
@ -1094,7 +1095,7 @@ void tst_QDebug::qDebugQFlags() const
{ {
QString file, function; QString file, function;
int line = 0; int line = 0;
QFlags<TestEnum> flags(Flag1 | Flag2); QFlags<TestEnum> flags(Flag1 | Flag2 | SignFlag);
MessageHandlerSetter mhs(myMessageHandler); MessageHandlerSetter mhs(myMessageHandler);
{ qDebug() << flags; } { qDebug() << flags; }
@ -1102,7 +1103,7 @@ void tst_QDebug::qDebugQFlags() const
file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO;
#endif #endif
QCOMPARE(s_msgType, QtDebugMsg); QCOMPARE(s_msgType, QtDebugMsg);
QCOMPARE(s_msg, QString::fromLatin1("QFlags(0x1|0x10)")); QCOMPARE(s_msg, QString::fromLatin1("QFlags(0x1|0x10|0x80000000)"));
QCOMPARE(QString::fromLatin1(s_file), file); QCOMPARE(QString::fromLatin1(s_file), file);
QCOMPARE(s_line, line); QCOMPARE(s_line, line);
QCOMPARE(QString::fromLatin1(s_function), function); QCOMPARE(QString::fromLatin1(s_function), function);