diff --git a/src/corelib/io/qdebug.cpp b/src/corelib/io/qdebug.cpp index d5bdfadba92..598355e4296 100644 --- a/src/corelib/io/qdebug.cpp +++ b/src/corelib/io/qdebug.cpp @@ -1329,9 +1329,19 @@ QDebugStateSaver::~QDebugStateSaver() */ void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, uint value) { - qt_QMetaEnum_flagDebugOperator(debug, sizeofT, value); + qt_QMetaEnum_flagDebugOperator(debug, sizeofT, quint64(value)); } +/*! + \internal + Ditto, for 64-bit. +*/ +void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, quint64 value) +{ + qt_QMetaEnum_flagDebugOperator(debug, sizeofT, value); +} + + #ifndef QT_NO_QOBJECT /*! \internal @@ -1449,7 +1459,7 @@ QDebug qt_QMetaEnum_flagDebugOperator(QDebug &debug, quint64 value, const QMetaO debug << '('; } - debug << me.valueToKeys(static_cast(value)); + debug << me.valueToKeys(value); if (enumScope) debug << ')'; diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h index 9b86c8eb1a1..c4be5955c8e 100644 --- a/src/corelib/io/qdebug.h +++ b/src/corelib/io/qdebug.h @@ -526,6 +526,7 @@ inline QDebug operator<<(QDebug debug, const QTaggedPointer &ptr) Q_CORE_EXPORT QDebug qt_QMetaEnum_debugOperator(QDebug&, qint64 value, const QMetaObject *meta, const char *name); Q_CORE_EXPORT QDebug qt_QMetaEnum_flagDebugOperator(QDebug &dbg, quint64 value, const QMetaObject *meta, const char *name); Q_CORE_EXPORT void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, uint value); +Q_CORE_EXPORT void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, quint64 value); template void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, Int value) @@ -556,9 +557,13 @@ inline QDebug operator<<(QDebug debug, Flags flags) using UInt = typename QIntegerForSizeof::Unsigned; #if !defined(QT_NO_QOBJECT) if constexpr (QtPrivate::IsQEnumHelper::Value || QtPrivate::IsQEnumHelper::Value) { + // if QFlags is a Q_FLAG, we always zero-extend; if not, we need to + // allow it to sign-extend if it is signed (see QMetaEnum::valueToKeys) + using Int = std::conditional_t::Value, UInt, + std::underlying_type_t>; const QMetaObject *obj = qt_getEnumMetaObject(T()); const char *name = qt_getEnumName(T()); - return qt_QMetaEnum_flagDebugOperator(debug, UInt(flags.toInt()), obj, name); + return qt_QMetaEnum_flagDebugOperator(debug, Int(flags.toInt()), obj, name); } else #endif { diff --git a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp index 5d4d4048129..fafda8bce87 100644 --- a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp +++ b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp @@ -54,9 +54,14 @@ class tst_QDebug: public QObject public: enum EnumType { EnumValue1 = 1, EnumValue2 = INT_MIN }; enum FlagType { EnumFlag1 = 1, EnumFlag2 = INT_MIN }; + enum Enum64Type { Enum64Value1 = 1, Enum64Value2 = Q_UINT64_C(0x1'0000'0002) }; + enum Flag64Type : qlonglong { Enum64Flag1 = 1, Enum64Flag2 = Q_UINT64_C(0x1'0000'0002) }; Q_ENUM(EnumType) + Q_ENUM(Enum64Type) Q_DECLARE_FLAGS(Flags, FlagType) Q_FLAG(Flags) + Q_DECLARE_FLAGS(Flags64, Flag64Type) + Q_FLAG(Flags64) private slots: void assignment() const; @@ -88,6 +93,7 @@ private slots: void qDebugQByteArray() const; void qDebugQByteArrayView() const; void qDebugQFlags() const; + void qDebugQFlags64() const; void qDebugStdChrono_data() const; void qDebugStdChrono() const; void qDebugStdOptional() const; @@ -106,6 +112,8 @@ private slots: void objcInObjcMode() const; #endif }; +Q_DECLARE_OPERATORS_FOR_FLAGS(tst_QDebug::Flags) +Q_DECLARE_OPERATORS_FOR_FLAGS(tst_QDebug::Flags64) void tst_QDebug::assignment() const { @@ -1096,8 +1104,9 @@ void tst_QDebug::qDebugQFlags() const QString file, function; int line = 0; QFlags flags(Flag1 | Flag2 | SignFlag); - MessageHandlerSetter mhs(myMessageHandler); + + // first, test QFlags on an enum where neither are Q_FLAG or Q_ENUM { qDebug() << flags; } #ifndef QT_NO_MESSAGELOGCONTEXT file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; @@ -1108,17 +1117,54 @@ void tst_QDebug::qDebugQFlags() const QCOMPARE(s_line, line); QCOMPARE(QString::fromLatin1(s_function), function); - // Test the output of QFlags with an enum not declared with Q_DECLARE_FLAGS and Q_FLAGS + // QFlags where the backing enum is a Q_ENUM but the QFlags isn't a QFLAG QFlags flags2(EnumValue2); qDebug() << flags2; QCOMPARE(s_msg, QString::fromLatin1("QFlags(EnumValue2)")); - // A now for one that was fully declared + // And now for one that was fully declared tst_QDebug::Flags flags3(EnumFlag1); qDebug() << flags3; QCOMPARE(s_msg, QString::fromLatin1("QFlags(EnumFlag1)")); } +enum class TestEnum64 : qulonglong { + Flag1 = 0x1, + Flag2 = Q_UINT64_C(0x8000'0000'0000'0000) +}; + +Q_DECLARE_FLAGS(TestFlags64, TestEnum64) +Q_DECLARE_OPERATORS_FOR_FLAGS(TestFlags64) + +void tst_QDebug::qDebugQFlags64() const +{ + QString file, function; + int line = 0; + QFlags flags(TestEnum64::Flag1 | TestEnum64::Flag2); + MessageHandlerSetter mhs(myMessageHandler); + + // first, test QFlags on an enum where neither are Q_FLAG or Q_ENUM + qDebug() << flags; +#ifndef QT_NO_MESSAGELOGCONTEXT + file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; +#endif + QCOMPARE(s_msgType, QtDebugMsg); + QCOMPARE(s_msg, "QFlags(0x1|0x8000000000000000)"); + QCOMPARE(QString::fromLatin1(s_file), file); + QCOMPARE(s_line, line); + QCOMPARE(QString::fromLatin1(s_function), function); + + // QFlags where the backing enum is a Q_ENUM but the QFlags isn't a QFLAG + QFlags flags2(Enum64Value2); + qDebug() << flags2; + QCOMPARE(s_msg, QString::fromLatin1("QFlags(Enum64Value2)")); + + // And now for one that was fully declared + tst_QDebug::Flags64 flags3(Enum64Flag1|Enum64Flag2); + qDebug() << flags3; + QCOMPARE(s_msg, QString::fromLatin1("QFlags(Enum64Flag1|Enum64Flag2)")); +} + using ToStringFunction = std::function; void tst_QDebug::qDebugStdChrono_data() const {