From b83de5f9a43b094bbb77b3aeea77983ea508a2b0 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 14 Mar 2023 15:48:49 +0100 Subject: [PATCH] moc: Record types of enumerations This will be helpful in a number of places, in particular in order to support enums of different sizes in QML. We record the type as string in the JSON output and as QMetaTypeInterface in the generated C++. Task-number: QTBUG-112180 Change-Id: I943fac67f8b25b013d3860301416cdd293c0c69e Reviewed-by: Shawn Rutledge Reviewed-by: Fabian Kosmale --- src/corelib/kernel/qmetaobject.cpp | 60 +++++++++- src/corelib/kernel/qmetaobject.h | 3 + src/corelib/kernel/qmetaobject_p.h | 3 +- src/corelib/kernel/qmetaobjectbuilder.cpp | 29 ++++- src/corelib/kernel/qmetaobjectbuilder_p.h | 3 + src/dbus/qdbusmetaobject.cpp | 2 +- src/tools/moc/generator.cpp | 17 ++- src/tools/moc/moc.cpp | 4 +- src/tools/moc/moc.h | 1 + .../tst_qmetaobjectbuilder.cpp | 22 ++++ tests/auto/tools/moc/allmocs_baseline_in.json | 64 +++++++++++ tests/auto/tools/moc/cxx11-enums.h | 16 +++ tests/auto/tools/moc/tst_moc.cpp | 108 +++++++++++++----- 13 files changed, 292 insertions(+), 40 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 09692600c31..63577aab2ea 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -419,10 +419,30 @@ QMetaType QMetaObject::metaType() const return QMetaType::fromName(className()); } else { /* in the metatype array, we store - idx: 0 propertyCount - 1 propertyCount - data:QMetaType(prop0), ..., QMetaType(propPropCount-1), QMetaType(class),... - */ - auto iface = this->d.metaTypes[d->propertyCount]; + + | index | data | + |----------------------------------------------------------------------| + | 0 | QMetaType(property0) | + | ... | ... | + | propertyCount - 1 | QMetaType(propertyCount - 1) | + | propertyCount | QMetaType(enumerator0) | + | ... | ... | + | propertyCount + enumeratorCount - 1 | QMetaType(enumeratorCount - 1) | + | propertyCount + enumeratorCount | QMetaType(class) | + + */ +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) + // Before revision 12 we only stored metatypes for enums if they showed + // up as types of properties or method arguments or return values. + // From revision 12 on, we always store them in a predictable place. + const qsizetype offset = d->revision < 12 + ? d->propertyCount + : d->propertyCount + d->enumeratorCount; +#else + const qsizetype offset = d->propertyCount + d->enumeratorCount; +#endif + + auto iface = this->d.metaTypes[offset]; if (iface && QtMetaTypePrivate::isInterfaceFor(iface)) return QMetaType(); // return invalid meta-type for namespaces if (iface) @@ -3036,6 +3056,33 @@ const char *QMetaEnum::enumName() const return rawStringData(mobj, data.alias()); } +/*! + Returns the meta type of the enum. + + If the QMetaObject this enum is part of was generated with Qt 6.5 or + earlier this will be the invalid metatype. + + \note This is the meta type of the enum itself, not of its underlying + numeric type. You can retrieve the meta type of the underlying type of the + enum using \l{QMetaType::underlyingType()}. + + \since 6.6 + \sa QMetaType::underlyingType() +*/ +QMetaType QMetaEnum::metaType() const +{ + if (!mobj) + return {}; + + const QMetaObjectPrivate *p = priv(mobj->d.data); +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) + if (p->revision < 12) + QMetaType(); +#endif + + return QMetaType(mobj->d.metaTypes[data.index(mobj) + p->propertyCount]); +} + /*! Returns the number of keys. @@ -3285,6 +3332,11 @@ QMetaEnum::QMetaEnum(const QMetaObject *mobj, int index) Q_ASSERT(index >= 0 && index < priv(mobj->d.data)->enumeratorCount); } +int QMetaEnum::Data::index(const QMetaObject *mobj) const +{ + return (d - mobj->d.data - priv(mobj->d.data)->enumeratorData) / Size; +} + /*! \fn QMetaEnum QMetaEnum::fromType() \since 5.5 diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index 3aa3a687c4f..b27a8636eb5 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -265,6 +265,8 @@ public: const char *name() const; const char *enumName() const; + QMetaType metaType() const; + bool isFlag() const; bool isScoped() const; @@ -302,6 +304,7 @@ private: quint32 flags() const { return d[2]; } qint32 keyCount() const { return static_cast(d[3]); } quint32 data() const { return d[4]; } + int index(const QMetaObject *mobj) const; const uint *d; }; diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index d05b727f521..224c9d53541 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -175,7 +175,8 @@ struct QMetaObjectPrivate // revision 10 is Qt 6.2: The metatype of the metaobject is stored in the metatypes array // and metamethods store a flag stating whether they are const // revision 11 is Qt 6.5: The metatype for void is stored in the metatypes array - enum { OutputRevision = 11 }; // Used by moc, qmetaobjectbuilder and qdbus + // revision 12 is Qt 6.6: It adds the metatype for enums + enum { OutputRevision = 12 }; // Used by moc, qmetaobjectbuilder and qdbus enum { IntsPerMethod = QMetaMethod::Data::Size }; enum { IntsPerEnum = QMetaEnum::Data::Size }; enum { IntsPerProperty = QMetaProperty::Data::Size }; diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index e61065f42f4..b471bf65466 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -159,6 +159,7 @@ public: QByteArray name; QByteArray enumName; + QMetaType metaType; bool isFlag; bool isScoped; QList keys; @@ -597,6 +598,7 @@ QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QMetaEnum &prototype) { QMetaEnumBuilder en = addEnumerator(prototype.name()); en.setEnumName(prototype.enumName()); + en.setMetaType(prototype.metaType()); en.setIsFlag(prototype.isFlag()); en.setIsScoped(prototype.isScoped()); int count = prototype.keyCount(); @@ -1170,7 +1172,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, - int(d->methods.size()) // return "parameters" don't have names - int(d->constructors.size()); // "this" parameters don't have names if constexpr (mode == Construct) { - static_assert(QMetaObjectPrivate::OutputRevision == 11, "QMetaObjectBuilder should generate the same version as moc"); + static_assert(QMetaObjectPrivate::OutputRevision == 12, "QMetaObjectBuilder should generate the same version as moc"); pmeta->revision = QMetaObjectPrivate::OutputRevision; pmeta->flags = d->flags.toInt(); pmeta->className = 0; // Class name is always the first string. @@ -2305,6 +2307,31 @@ void QMetaEnumBuilder::setEnumName(const QByteArray &alias) d->enumName = alias; } +/*! + Returns the meta type of the enumerator. + + \since 6.6 +*/ +QMetaType QMetaEnumBuilder::metaType() const +{ + if (QMetaEnumBuilderPrivate *d = d_func()) + return d->metaType; + return QMetaType(); +} + +/*! + Sets this enumerator to have the given \c metaType. + + \since 6.6 + \sa metaType() +*/ +void QMetaEnumBuilder::setMetaType(QMetaType metaType) +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + d->metaType = metaType; +} + /*! Returns \c true if this enumerator is used as a flag; otherwise returns false. diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h index 99eaca2ac9e..b52487986ce 100644 --- a/src/corelib/kernel/qmetaobjectbuilder_p.h +++ b/src/corelib/kernel/qmetaobjectbuilder_p.h @@ -258,6 +258,9 @@ public: QByteArray enumName() const; void setEnumName(const QByteArray &alias); + QMetaType metaType() const; + void setMetaType(QMetaType metaType); + bool isFlag() const; void setIsFlag(bool value); diff --git a/src/dbus/qdbusmetaobject.cpp b/src/dbus/qdbusmetaobject.cpp index 054bc448e6a..d2ca9df8690 100644 --- a/src/dbus/qdbusmetaobject.cpp +++ b/src/dbus/qdbusmetaobject.cpp @@ -391,7 +391,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) - methods.size(); // ditto QDBusMetaObjectPrivate *header = reinterpret_cast(idata.data()); - static_assert(QMetaObjectPrivate::OutputRevision == 11, "QtDBus meta-object generator should generate the same version as moc"); + static_assert(QMetaObjectPrivate::OutputRevision == 12, "QtDBus meta-object generator should generate the same version as moc"); header->revision = QMetaObjectPrivate::OutputRevision; header->className = 0; header->classInfoCount = 0; diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index ca6a4181b60..cef460b7ddb 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -378,7 +378,7 @@ void Generator::generateCode() int enumsIndex = index; for (int i = 0; i < cdef->enumList.size(); ++i) - index += 5 + (cdef->enumList.at(i).values.size() * 2); + index += QMetaObjectPrivate::IntsPerEnum + (cdef->enumList.at(i).values.size() * 2); fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? int(cdef->constructorList.size()) : 0, isConstructible ? index : 0); @@ -397,8 +397,8 @@ void Generator::generateCode() // generateClassInfos(); - // all property metatypes, + 1 for the type of the current class itself - int initialMetaTypeOffset = cdef->propertyList.size() + 1; + // all property metatypes + all enum metatypes + 1 for the type of the current class itself + int initialMetaTypeOffset = cdef->propertyList.size() + cdef->enumList.size() + 1; // // Build signals array first, otherwise the signal indices would be wrong @@ -576,6 +576,15 @@ void Generator::generateCode() comma = ","; } + // metatypes for enums + for (int i = 0; i < cdef->enumList.size(); ++i) { + const EnumDef &e = cdef->enumList.at(i); + fprintf(out, "%s\n // enum '%s'\n %s", + comma, e.name.constData(), + stringForType(cdef->classname % "::" % e.name, true).constData()); + comma = ","; + } + // type name for the Q_OJBECT/GADGET itself, void for namespaces auto ownType = !cdef->hasQNamespace ? cdef->classname.data() : "void"; fprintf(out, "%s\n // Q_OBJECT / Q_GADGET\n %s", @@ -943,7 +952,7 @@ void Generator::generateEnums(int index) return; fprintf(out, "\n // enums: name, alias, flags, count, data\n"); - index += 5 * cdef->enumList.size(); + index += QMetaObjectPrivate::IntsPerEnum * cdef->enumList.size(); int i; for (i = 0; i < cdef->enumList.size(); ++i) { const EnumDef &e = cdef->enumList.at(i); diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 64e98ae63be..743742ba970 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -243,7 +243,7 @@ bool Moc::parseEnum(EnumDef *def) } if (test(COLON)) { // C++11 strongly typed enum // enum Foo : unsigned long { ... }; - parseType(); //ignore the result + def->type = normalizeType(parseType().name); } if (!test(LBRACE)) return false; @@ -2107,6 +2107,8 @@ QJsonObject EnumDef::toJson(const ClassDef &cdef) const def["name"_L1] = QString::fromUtf8(name); if (!enumName.isEmpty()) def["alias"_L1] = QString::fromUtf8(enumName); + if (!type.isEmpty()) + def["type"_L1] = QString::fromUtf8(type); def["isFlag"_L1] = cdef.enumDeclarations.value(name); def["isClass"_L1] = isEnumClass; diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index edeac03b82b..5df15409a7c 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -43,6 +43,7 @@ struct EnumDef { QByteArray name; QByteArray enumName; + QByteArray type; QList values; bool isEnumClass; // c++11 enum class EnumDef() : isEnumClass(false) {} diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 38c896b6a35..42df2987700 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -779,6 +779,26 @@ void tst_QMetaObjectBuilder::notifySignal() void tst_QMetaObjectBuilder::enumerator() { + static const QtPrivate::QMetaTypeInterface fooFlagMetaType = { + 0, + 8, + 8, + QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration | QMetaType::RelocatableType, + {}, + nullptr, + "fooFlag", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + }; + QMetaObjectBuilder builder; // Add an enumerator and check its attributes. @@ -810,6 +830,7 @@ void tst_QMetaObjectBuilder::enumerator() enum1.setIsFlag(true); enum1.setIsScoped(true); enum1.setEnumName(QByteArrayLiteral("fooFlag")); + enum1.setMetaType(QMetaType(&fooFlagMetaType)); QCOMPARE(enum1.addKey("ABC", 0), 0); QCOMPARE(enum1.addKey("DEF", 1), 1); QCOMPARE(enum1.addKey("GHI", -1), 2); @@ -819,6 +840,7 @@ void tst_QMetaObjectBuilder::enumerator() QVERIFY(enum1.isFlag()); QVERIFY(enum1.isScoped()); QCOMPARE(enum1.enumName(), QByteArray("fooFlag")); + QCOMPARE(enum1.metaType(), QMetaType(&fooFlagMetaType)); QCOMPARE(enum1.keyCount(), 3); QCOMPARE(enum1.index(), 0); QCOMPARE(enum1.key(0), QByteArray("ABC")); diff --git a/tests/auto/tools/moc/allmocs_baseline_in.json b/tests/auto/tools/moc/allmocs_baseline_in.json index 46f643d09b0..50dbb200d2d 100644 --- a/tests/auto/tools/moc/allmocs_baseline_in.json +++ b/tests/auto/tools/moc/allmocs_baseline_in.json @@ -91,6 +91,7 @@ "isClass": false, "isFlag": false, "name": "TypedEnum", + "type": "char", "values": [ "B0", "B1", @@ -102,6 +103,7 @@ "isClass": true, "isFlag": false, "name": "TypedEnumClass", + "type": "char", "values": [ "C0", "C1", @@ -147,6 +149,7 @@ "isClass": true, "isFlag": false, "name": "TypedEnumStruct", + "type": "char", "values": [ "H0", "H1", @@ -188,6 +191,7 @@ "isClass": false, "isFlag": false, "name": "TypedEnum", + "type": "char", "values": [ "B0", "B1", @@ -199,6 +203,7 @@ "isClass": true, "isFlag": false, "name": "TypedEnumClass", + "type": "char", "values": [ "C0", "C1", @@ -232,6 +237,65 @@ ], "gadget": true, "qualifiedClassName": "CXX11Enums2" + }, + { + "className": "CXX11Enums3", + "enums": [ + { + "isClass": true, + "isFlag": false, + "name": "EnumClass", + "values": [ + "A0", + "A1", + "A2", + "A3" + ] + }, + { + "isClass": false, + "isFlag": false, + "name": "TypedEnum", + "type": "char", + "values": [ + "B0", + "B1", + "B2", + "B3" + ] + }, + { + "isClass": true, + "isFlag": false, + "name": "TypedEnumClass", + "type": "char", + "values": [ + "C0", + "C1", + "C2", + "C3" + ] + }, + { + "isClass": false, + "isFlag": false, + "name": "NormalEnum", + "values": [ + "D2", + "D3", + "D0", + "D1" + ] + } + ], + "object": true, + "qualifiedClassName": "CXX11Enums3", + "superClasses": [ + { + "access": "public", + "name": "QObject" + } + ] } ], "inputFile": "cxx11-enums.h", diff --git a/tests/auto/tools/moc/cxx11-enums.h b/tests/auto/tools/moc/cxx11-enums.h index cd82a5df6d2..821cd038636 100644 --- a/tests/auto/tools/moc/cxx11-enums.h +++ b/tests/auto/tools/moc/cxx11-enums.h @@ -47,4 +47,20 @@ public: Q_FLAGS(ClassFlags) }; +class CXX11Enums3 : public QObject +{ + Q_OBJECT +public: + enum class EnumClass { A0, A1, A2, A3 }; + enum TypedEnum : char { B0, B1 , B2, B3 }; + enum class TypedEnumClass : char { C0, C1, C2, C3 }; + enum NormalEnum { D2 = 2, D3, D0 =0 , D1 }; + enum class ClassFlag { F0 = 1, F1 = 2, F2 = 4, F3 = 8 }; + + Q_ENUM(EnumClass) + Q_ENUM(TypedEnum) + Q_ENUM(TypedEnumClass) + Q_ENUM(NormalEnum) +}; + #endif // CXX11_ENUMS_H diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index e2844e29d1d..6a2962d609e 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -98,6 +98,12 @@ public: Key2 }; Q_ENUM(TestGEnum2) + + enum TestGEnum3: quint8 { + Key1 = 23, + Key2 + }; + Q_ENUM(TestGEnum3) }; } @@ -116,6 +122,12 @@ namespace TestQNamespace { }; Q_ENUM_NS(TestEnum2) + enum TestEnum3: qint8 { + Key1 = 23, + Key2 + }; + Q_ENUM_NS(TestEnum3) + // try to dizzy moc by adding a struct in between struct TestGadget { Q_GADGET @@ -128,8 +140,13 @@ namespace TestQNamespace { Key1 = 23, Key2 }; + enum TestGEnum3: qint16 { + Key1 = 33, + Key2 + }; Q_ENUM(TestGEnum1) Q_ENUM(TestGEnum2) + Q_ENUM(TestGEnum3) }; struct TestGadgetExport { @@ -146,6 +163,12 @@ namespace TestQNamespace { Key2 }; Q_ENUM(TestGeEnum2) + enum TestGeEnum3: quint16 { + Key1 = 26, + Key2 + }; + Q_ENUM(TestGeEnum3) + }; enum class TestFlag1 { @@ -2428,23 +2451,29 @@ void tst_Moc::cxx11Enums_data() QTest::addColumn("enumName"); QTest::addColumn("prefix"); QTest::addColumn("isScoped"); + QTest::addColumn("isTyped"); const QMetaObject *meta1 = &CXX11Enums::staticMetaObject; const QMetaObject *meta2 = &CXX11Enums2::staticMetaObject; + const QMetaObject *meta3 = &CXX11Enums3::staticMetaObject; - QTest::newRow("EnumClass") << meta1 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true; - QTest::newRow("EnumClass 2") << meta2 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true; - QTest::newRow("TypedEnum") << meta1 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false; - QTest::newRow("TypedEnum 2") << meta2 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false; - QTest::newRow("TypedEnumClass") << meta1 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true; - QTest::newRow("TypedEnumClass 2") << meta2 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true; - QTest::newRow("NormalEnum") << meta1 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false; - QTest::newRow("NormalEnum 2") << meta2 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false; - QTest::newRow("ClassFlags") << meta1 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true; - QTest::newRow("ClassFlags 2") << meta2 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true; - QTest::newRow("EnumStruct") << meta1 << QByteArray("EnumStruct") << QByteArray("EnumStruct") << 'G' << true; - QTest::newRow("TypedEnumStruct") << meta1 << QByteArray("TypedEnumStruct") << QByteArray("TypedEnumStruct") << 'H' << true; - QTest::newRow("StructFlags") << meta1 << QByteArray("StructFlags") << QByteArray("StructFlag") << 'I' << true; + QTest::newRow("EnumClass") << meta1 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true << false; + QTest::newRow("EnumClass 2") << meta2 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true << false; + QTest::newRow("EnumClass 3") << meta3 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true << false; + QTest::newRow("TypedEnum") << meta1 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false << true; + QTest::newRow("TypedEnum 2") << meta2 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false << true; + QTest::newRow("TypedEnum 3") << meta3 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false << true; + QTest::newRow("TypedEnumClass") << meta1 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true << true; + QTest::newRow("TypedEnumClass 2") << meta2 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true << true; + QTest::newRow("TypedEnumClass 3") << meta3 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true << true; + QTest::newRow("NormalEnum") << meta1 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false << false; + QTest::newRow("NormalEnum 2") << meta2 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false << false; + QTest::newRow("NormalEnum 3") << meta3 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false << false; + QTest::newRow("ClassFlags") << meta1 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true << false; + QTest::newRow("ClassFlags 2") << meta2 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true << false; + QTest::newRow("EnumStruct") << meta1 << QByteArray("EnumStruct") << QByteArray("EnumStruct") << 'G' << true << false; + QTest::newRow("TypedEnumStruct") << meta1 << QByteArray("TypedEnumStruct") << QByteArray("TypedEnumStruct") << 'H' << true << true; + QTest::newRow("StructFlags") << meta1 << QByteArray("StructFlags") << QByteArray("StructFlag") << 'I' << true << false; } void tst_Moc::cxx11Enums() @@ -2456,24 +2485,39 @@ void tst_Moc::cxx11Enums() QFETCH(QByteArray, enumName); QFETCH(char, prefix); QFETCH(bool, isScoped); + QFETCH(bool, isTyped); int idx = meta->indexOfEnumerator(typeName); QVERIFY(idx != -1); QCOMPARE(meta->indexOfEnumerator(enumName), idx); - QCOMPARE(meta->enumerator(idx).enclosingMetaObject(), meta); - QCOMPARE(meta->enumerator(idx).isValid(), true); - QCOMPARE(meta->enumerator(idx).keyCount(), 4); - QCOMPARE(meta->enumerator(idx).name(), typeName.constData()); - QCOMPARE(meta->enumerator(idx).enumName(), enumName.constData()); - bool isFlag = meta->enumerator(idx).isFlag(); + const QMetaEnum metaEnum = meta->enumerator(idx); + QCOMPARE(metaEnum.enclosingMetaObject(), meta); + QCOMPARE(metaEnum.isValid(), true); + QCOMPARE(metaEnum.keyCount(), 4); + QCOMPARE(metaEnum.name(), typeName.constData()); + QCOMPARE(metaEnum.enumName(), enumName.constData()); + + const QMetaType metaType = metaEnum.metaType(); + const bool isUnsigned = metaType.flags() & QMetaType::IsUnsignedEnumeration; + if (isTyped) { + QCOMPARE(metaType.sizeOf(), sizeof(char)); + QCOMPARE(isUnsigned, !std::is_signed_v); + } else if (isScoped) { + QCOMPARE(metaType.sizeOf(), sizeof(int)); + QCOMPARE(isUnsigned, !std::is_signed_v); + } else { + // underlying type is implementation defined + } + + bool isFlag = metaEnum.isFlag(); for (int i = 0; i < 4; i++) { QByteArray v = prefix + QByteArray::number(i); const int value = isFlag ? (1 << i) : i; - QCOMPARE(meta->enumerator(idx).keyToValue(v), value); - QCOMPARE(meta->enumerator(idx).valueToKey(value), v.constData()); + QCOMPARE(metaEnum.keyToValue(v), value); + QCOMPARE(metaEnum.valueToKey(value), v.constData()); } - QCOMPARE(meta->enumerator(idx).isScoped(), isScoped); + QCOMPARE(metaEnum.isScoped(), isScoped); } void tst_Moc::cxx11TrailingReturn() @@ -3994,10 +4038,12 @@ void tst_Moc::optionsFileError() } static void checkEnum(const QMetaEnum &enumerator, const QByteArray &name, - const QList> &keys) + const QList> &keys, + const QMetaType underlyingType = QMetaType::fromType()) { QCOMPARE(name, QByteArray{enumerator.name()}); QCOMPARE(keys.size(), enumerator.keyCount()); + QCOMPARE(underlyingType, enumerator.metaType().underlyingType()); for (int i = 0; i < enumerator.keyCount(); ++i) { QCOMPARE(keys[i].first, QByteArray{enumerator.key(i)}); QCOMPARE(keys[i].second, enumerator.value(i)); @@ -4014,27 +4060,33 @@ public: void tst_Moc::testQNamespace() { - QCOMPARE(TestQNamespace::staticMetaObject.enumeratorCount(), 4); + QCOMPARE(TestQNamespace::staticMetaObject.enumeratorCount(), 5); checkEnum(TestQNamespace::staticMetaObject.enumerator(0), "TestEnum1", {{"Key1", 11}, {"Key2", 12}}); checkEnum(TestQNamespace::staticMetaObject.enumerator(1), "TestEnum2", {{"Key1", 17}, {"Key2", 18}}); - checkEnum(TestQNamespace::staticMetaObject.enumerator(2), "TestFlag1", + checkEnum(TestQNamespace::staticMetaObject.enumerator(2), "TestEnum3", + {{"Key1", 23}, {"Key2", 24}}, QMetaType::fromType()); + checkEnum(TestQNamespace::staticMetaObject.enumerator(3), "TestFlag1", {{"None", 0}, {"Flag1", 1}, {"Flag2", 2}, {"Any", 1 | 2}}); - checkEnum(TestQNamespace::staticMetaObject.enumerator(3), "TestFlag2", + checkEnum(TestQNamespace::staticMetaObject.enumerator(4), "TestFlag2", {{"None", 0}, {"Flag1", 4}, {"Flag2", 8}, {"Any", 4 | 8}}); - QCOMPARE(TestQNamespace::TestGadget::staticMetaObject.enumeratorCount(), 2); + QCOMPARE(TestQNamespace::TestGadget::staticMetaObject.enumeratorCount(), 3); checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(0), "TestGEnum1", {{"Key1", 13}, {"Key2", 14}}); checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(1), "TestGEnum2", {{"Key1", 23}, {"Key2", 24}}); + checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(2), "TestGEnum3", + {{"Key1", 33}, {"Key2", 34}}, QMetaType::fromType()); - QCOMPARE(TestQNamespace::TestGadgetExport::staticMetaObject.enumeratorCount(), 2); + QCOMPARE(TestQNamespace::TestGadgetExport::staticMetaObject.enumeratorCount(), 3); checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(0), "TestGeEnum1", {{"Key1", 20}, {"Key2", 21}}); checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(1), "TestGeEnum2", {{"Key1", 23}, {"Key2", 24}}); + checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(2), "TestGeEnum3", + {{"Key1", 26}, {"Key2", 27}}, QMetaType::fromType()); QMetaEnum meta = QMetaEnum::fromType(); QVERIFY(meta.isValid());