From 7779400ba6fee98b1f90702f92c17a5a4089c5ce Mon Sep 17 00:00:00 2001 From: Ahmad Samir Date: Wed, 7 Jun 2023 16:34:45 +0300 Subject: [PATCH] Moc: fix generated code for nested enum class corner case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes an issue with generated code where the name of an enclosing namespace is identical to an enum class type, when Q_ENUM_NS is used. Consider: namespace a { Q_NAMESPACE namespace b { enum class b { Key, Key2 }; Q_ENUM_NS(b); } } moc generated code such as: Q_CONSTINIT const QMetaObject a::b::staticMetaObject = { { ... qt_incomplete_metaTypeArray, // Q_OBJECT / Q_GADGET QtPrivate::TypeAndForceComplete >, nullptr } }; which confused the compiler: error: ‘b’ is not a member of ‘a::b::b 83 | QtPrivate::TypeAndForceComplete, Fixes: QTBUG-112996 Pick-to: 6.6 Change-Id: I37aee83c32efe96cc9d6c2bd0bdb9ba80bb7b8a7 Reviewed-by: Fabian Kosmale --- src/tools/moc/generator.cpp | 3 +-- src/tools/moc/moc.cpp | 19 ++++++++++++++ src/tools/moc/moc.h | 1 + tests/auto/tools/moc/tst_moc.cpp | 44 ++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index f3a53dfccd4..b083ed63b7a 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -525,8 +525,7 @@ void Generator::generateCode() // metatypes for enums for (const EnumDef &e : std::as_const(cdef->enumList)) { fprintf(out, "%s\n // enum '%s'\n %s", - comma, e.name.constData(), - stringForType(cdef->classname % "::" % e.name, true).constData()); + comma, e.name.constData(), stringForType(e.qualifiedType(cdef), true).constData()); comma = ","; } diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 058df18f428..1d1948525c2 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -2187,4 +2187,23 @@ QJsonObject EnumDef::toJson(const ClassDef &cdef) const return def; } +QByteArray EnumDef::qualifiedType(const ClassDef *cdef) const +{ + if (name == cdef->classname) { + // The name of the enclosing namespace is the same as the enum class name + if (cdef->qualified.contains("::")) { + // QTBUG-112996, fully qualify by using cdef->qualified to disambiguate enum + // class name and enclosing namespace, e.g.: + // namespace A { namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) } } + return cdef->qualified % "::" % name; + } else { + // Just "B"; otherwise the compiler complains about the type "B::B" inside + // "B::staticMetaObject" in the generated code; e.g.: + // namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) } + return name; + } + } + return cdef->classname % "::" % name; +} + QT_END_NAMESPACE diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index 04f1c835ced..a3ddb835b51 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -48,6 +48,7 @@ struct EnumDef bool isEnumClass; // c++11 enum class EnumDef() : isEnumClass(false) {} QJsonObject toJson(const ClassDef &cdef) const; + QByteArray qualifiedType(const ClassDef *cdef) const; }; Q_DECLARE_TYPEINFO(EnumDef, Q_RELOCATABLE_TYPE); diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index a509c0ddae0..1929ad3fbaa 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -203,6 +203,27 @@ namespace TestQNamespace { Q_FLAG_NS(TestFlag2) } +namespace TestSameEnumNamespace { + Q_NAMESPACE + + enum class TestSameEnumNamespace { + Key1 = 1, + Key2 = 2, + }; + Q_ENUM_NS(TestSameEnumNamespace) +} + +namespace TestNestedSameEnumNamespace { +namespace a { + Q_NAMESPACE + // enum class with the same name as the enclosing nested namespace + enum class a { + Key11 = 11, + Key12 = 12, + }; + Q_ENUM_NS(a) +} +} namespace TestExportNamespace { Q_NAMESPACE_EXPORT(TESTEXPORTMACRO) @@ -789,6 +810,7 @@ private slots: void optionsFileError_data(); void optionsFileError(); void testQNamespace(); + void testNestedQNamespace(); void cxx17Namespaces(); void cxxAttributes(); void mocJsonOutput(); @@ -4081,6 +4103,28 @@ public: FooNamespace::Enum1 prop() { return FooNamespace::Enum1::Key2; } }; +void tst_Moc::testNestedQNamespace() +{ + QCOMPARE(TestSameEnumNamespace::staticMetaObject.enumeratorCount(), 1); + checkEnum(TestSameEnumNamespace::staticMetaObject.enumerator(0), "TestSameEnumNamespace", + {{"Key1", 1}, {"Key2", 2}}); + QMetaEnum meta1 = QMetaEnum::fromType(); + QVERIFY(meta1.isValid()); + QCOMPARE(meta1.name(), "TestSameEnumNamespace"); + QCOMPARE(meta1.enclosingMetaObject(), &TestSameEnumNamespace::staticMetaObject); + QCOMPARE(meta1.keyCount(), 2); + + // QTBUG-112996 + QCOMPARE(TestNestedSameEnumNamespace::a::staticMetaObject.enumeratorCount(), 1); + checkEnum(TestNestedSameEnumNamespace::a::staticMetaObject.enumerator(0), "a", + {{"Key11", 11}, {"Key12", 12}}); + QMetaEnum meta2 = QMetaEnum::fromType(); + QVERIFY(meta2.isValid()); + QCOMPARE(meta2.name(), "a"); + QCOMPARE(meta2.enclosingMetaObject(), &TestNestedSameEnumNamespace::a::staticMetaObject); + QCOMPARE(meta2.keyCount(), 2); +} + void tst_Moc::testQNamespace() { QCOMPARE(TestQNamespace::staticMetaObject.enumeratorCount(), 5);